Browse code

removed Telnyx Plivo Twilio Flowroute appinfo/info.xml appinfo/signature.json README.md lib/Controller/AuthorApiController.php

DoubleBastionAdmin authored on 20/08/2022 16:26:33
Showing 1 changed files
1 1
deleted file mode 100644
... ...
@@ -1,512 +0,0 @@
1
-<?php
2
-
3
-namespace Firebase\JWT;
4
-
5
-use \DomainException;
6
-use \InvalidArgumentException;
7
-use \UnexpectedValueException;
8
-use \DateTime;
9
-
10
-/**
11
- * JSON Web Token implementation, based on this spec:
12
- * https://tools.ietf.org/html/rfc7519
13
- *
14
- * PHP version 5
15
- *
16
- * @category Authentication
17
- * @package  Authentication_JWT
18
- * @author   Neuman Vong <neuman@twilio.com>
19
- * @author   Anant Narayanan <anant@php.net>
20
- * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
21
- * @link     https://github.com/firebase/php-jwt
22
- */
23
-class JWT
24
-{
25
-    const ASN1_INTEGER = 0x02;
26
-    const ASN1_SEQUENCE = 0x10;
27
-    const ASN1_BIT_STRING = 0x03;
28
-
29
-    /**
30
-     * When checking nbf, iat or expiration times,
31
-     * we want to provide some extra leeway time to
32
-     * account for clock skew.
33
-     */
34
-    public static $leeway = 0;
35
-
36
-    /**
37
-     * Allow the current timestamp to be specified.
38
-     * Useful for fixing a value within unit testing.
39
-     *
40
-     * Will default to PHP time() value if null.
41
-     */
42
-    public static $timestamp = null;
43
-
44
-    public static $supported_algs = array(
45
-        'ES256' => array('openssl', 'SHA256'),
46
-        'HS256' => array('hash_hmac', 'SHA256'),
47
-        'HS384' => array('hash_hmac', 'SHA384'),
48
-        'HS512' => array('hash_hmac', 'SHA512'),
49
-        'RS256' => array('openssl', 'SHA256'),
50
-        'RS384' => array('openssl', 'SHA384'),
51
-        'RS512' => array('openssl', 'SHA512'),
52
-    );
53
-
54
-    /**
55
-     * Decodes a JWT string into a PHP object.
56
-     *
57
-     * @param string                    $jwt            The JWT
58
-     * @param string|array|resource     $key            The key, or map of keys.
59
-     *                                                  If the algorithm used is asymmetric, this is the public key
60
-     * @param array                     $allowed_algs   List of supported verification algorithms
61
-     *                                                  Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
62
-     *
63
-     * @return object The JWT's payload as a PHP object
64
-     *
65
-     * @throws UnexpectedValueException     Provided JWT was invalid
66
-     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
67
-     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
68
-     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
69
-     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
70
-     *
71
-     * @uses jsonDecode
72
-     * @uses urlsafeB64Decode
73
-     */
74
-    public static function decode($jwt, $key, array $allowed_algs = array())
75
-    {
76
-        $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
77
-
78
-        if (empty($key)) {
79
-            throw new InvalidArgumentException('Key may not be empty');
80
-        }
81
-        $tks = \explode('.', $jwt);
82
-        if (\count($tks) != 3) {
83
-            throw new UnexpectedValueException('Wrong number of segments');
84
-        }
85
-        list($headb64, $bodyb64, $cryptob64) = $tks;
86
-        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
87
-            throw new UnexpectedValueException('Invalid header encoding');
88
-        }
89
-        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
90
-            throw new UnexpectedValueException('Invalid claims encoding');
91
-        }
92
-        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
93
-            throw new UnexpectedValueException('Invalid signature encoding');
94
-        }
95
-        if (empty($header->alg)) {
96
-            throw new UnexpectedValueException('Empty algorithm');
97
-        }
98
-        if (empty(static::$supported_algs[$header->alg])) {
99
-            throw new UnexpectedValueException('Algorithm not supported');
100
-        }
101
-        if (!\in_array($header->alg, $allowed_algs)) {
102
-            throw new UnexpectedValueException('Algorithm not allowed');
103
-        }
104
-        if ($header->alg === 'ES256') {
105
-            // OpenSSL expects an ASN.1 DER sequence for ES256 signatures
106
-            $sig = self::signatureToDER($sig);
107
-        }
108
-
109
-        if (\is_array($key) || $key instanceof \ArrayAccess) {
110
-            if (isset($header->kid)) {
111
-                if (!isset($key[$header->kid])) {
112
-                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
113
-                }
114
-                $key = $key[$header->kid];
115
-            } else {
116
-                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
117
-            }
118
-        }
119
-
120
-        // Check the signature
121
-        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
122
-            throw new SignatureInvalidException('Signature verification failed');
123
-        }
124
-
125
-        // Check the nbf if it is defined. This is the time that the
126
-        // token can actually be used. If it's not yet that time, abort.
127
-        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
128
-            throw new BeforeValidException(
129
-                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
130
-            );
131
-        }
132
-
133
-        // Check that this token has been created before 'now'. This prevents
134
-        // using tokens that have been created for later use (and haven't
135
-        // correctly used the nbf claim).
136
-        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
137
-            throw new BeforeValidException(
138
-                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
139
-            );
140
-        }
141
-
142
-        // Check if this token has expired.
143
-        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
144
-            throw new ExpiredException('Expired token');
145
-        }
146
-
147
-        return $payload;
148
-    }
149
-
150
-    /**
151
-     * Converts and signs a PHP object or array into a JWT string.
152
-     *
153
-     * @param object|array  $payload    PHP object or array
154
-     * @param string        $key        The secret key.
155
-     *                                  If the algorithm used is asymmetric, this is the private key
156
-     * @param string        $alg        The signing algorithm.
157
-     *                                  Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
158
-     * @param mixed         $keyId
159
-     * @param array         $head       An array with header elements to attach
160
-     *
161
-     * @return string A signed JWT
162
-     *
163
-     * @uses jsonEncode
164
-     * @uses urlsafeB64Encode
165
-     */
166
-    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
167
-    {
168
-        $header = array('typ' => 'JWT', 'alg' => $alg);
169
-        if ($keyId !== null) {
170
-            $header['kid'] = $keyId;
171
-        }
172
-        if (isset($head) && \is_array($head)) {
173
-            $header = \array_merge($head, $header);
174
-        }
175
-        $segments = array();
176
-        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
177
-        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
178
-        $signing_input = \implode('.', $segments);
179
-
180
-        $signature = static::sign($signing_input, $key, $alg);
181
-        $segments[] = static::urlsafeB64Encode($signature);
182
-
183
-        return \implode('.', $segments);
184
-    }
185
-
186
-    /**
187
-     * Sign a string with a given key and algorithm.
188
-     *
189
-     * @param string            $msg    The message to sign
190
-     * @param string|resource   $key    The secret key
191
-     * @param string            $alg    The signing algorithm.
192
-     *                                  Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
193
-     *
194
-     * @return string An encrypted message
195
-     *
196
-     * @throws DomainException Unsupported algorithm was specified
197
-     */
198
-    public static function sign($msg, $key, $alg = 'HS256')
199
-    {
200
-        if (empty(static::$supported_algs[$alg])) {
201
-            throw new DomainException('Algorithm not supported');
202
-        }
203
-        list($function, $algorithm) = static::$supported_algs[$alg];
204
-        switch ($function) {
205
-            case 'hash_hmac':
206
-                return \hash_hmac($algorithm, $msg, $key, true);
207
-            case 'openssl':
208
-                $signature = '';
209
-                $success = \openssl_sign($msg, $signature, $key, $algorithm);
210
-                if (!$success) {
211
-                    throw new DomainException("OpenSSL unable to sign data");
212
-                } else {
213
-                    if ($alg === 'ES256') {
214
-                        $signature = self::signatureFromDER($signature, 256);
215
-                    }
216
-                    return $signature;
217
-                }
218
-        }
219
-    }
220
-
221
-    /**
222
-     * Verify a signature with the message, key and method. Not all methods
223
-     * are symmetric, so we must have a separate verify and sign method.
224
-     *
225
-     * @param string            $msg        The original message (header and body)
226
-     * @param string            $signature  The original signature
227
-     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
228
-     * @param string            $alg        The algorithm
229
-     *
230
-     * @return bool
231
-     *
232
-     * @throws DomainException Invalid Algorithm or OpenSSL failure
233
-     */
234
-    private static function verify($msg, $signature, $key, $alg)
235
-    {
236
-        if (empty(static::$supported_algs[$alg])) {
237
-            throw new DomainException('Algorithm not supported');
238
-        }
239
-
240
-        list($function, $algorithm) = static::$supported_algs[$alg];
241
-        switch ($function) {
242
-            case 'openssl':
243
-                $success = \openssl_verify($msg, $signature, $key, $algorithm);
244
-                if ($success === 1) {
245
-                    return true;
246
-                } elseif ($success === 0) {
247
-                    return false;
248
-                }
249
-                // returns 1 on success, 0 on failure, -1 on error.
250
-                throw new DomainException(
251
-                    'OpenSSL error: ' . \openssl_error_string()
252
-                );
253
-            case 'hash_hmac':
254
-            default:
255
-                $hash = \hash_hmac($algorithm, $msg, $key, true);
256
-                if (\function_exists('hash_equals')) {
257
-                    return \hash_equals($signature, $hash);
258
-                }
259
-                $len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
260
-
261
-                $status = 0;
262
-                for ($i = 0; $i < $len; $i++) {
263
-                    $status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
264
-                }
265
-                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
266
-
267
-                return ($status === 0);
268
-        }
269
-    }
270
-
271
-    /**
272
-     * Decode a JSON string into a PHP object.
273
-     *
274
-     * @param string $input JSON string
275
-     *
276
-     * @return object Object representation of JSON string
277
-     *
278
-     * @throws DomainException Provided string was invalid JSON
279
-     */
280
-    public static function jsonDecode($input)
281
-    {
282
-        if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
283
-            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
284
-             * to specify that large ints (like Steam Transaction IDs) should be treated as
285
-             * strings, rather than the PHP default behaviour of converting them to floats.
286
-             */
287
-            $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
288
-        } else {
289
-            /** Not all servers will support that, however, so for older versions we must
290
-             * manually detect large ints in the JSON string and quote them (thus converting
291
-             *them to strings) before decoding, hence the preg_replace() call.
292
-             */
293
-            $max_int_length = \strlen((string) PHP_INT_MAX) - 1;
294
-            $json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
295
-            $obj = \json_decode($json_without_bigints);
296
-        }
297
-
298
-        if ($errno = \json_last_error()) {
299
-            static::handleJsonError($errno);
300
-        } elseif ($obj === null && $input !== 'null') {
301
-            throw new DomainException('Null result with non-null input');
302
-        }
303
-        return $obj;
304
-    }
305
-
306
-    /**
307
-     * Encode a PHP object into a JSON string.
308
-     *
309
-     * @param object|array $input A PHP object or array
310
-     *
311
-     * @return string JSON representation of the PHP object or array
312
-     *
313
-     * @throws DomainException Provided object could not be encoded to valid JSON
314
-     */
315
-    public static function jsonEncode($input)
316
-    {
317
-        $json = \json_encode($input);
318
-        if ($errno = \json_last_error()) {
319
-            static::handleJsonError($errno);
320
-        } elseif ($json === 'null' && $input !== null) {
321
-            throw new DomainException('Null result with non-null input');
322
-        }
323
-        return $json;
324
-    }
325
-
326
-    /**
327
-     * Decode a string with URL-safe Base64.
328
-     *
329
-     * @param string $input A Base64 encoded string
330
-     *
331
-     * @return string A decoded string
332
-     */
333
-    public static function urlsafeB64Decode($input)
334
-    {
335
-        $remainder = \strlen($input) % 4;
336
-        if ($remainder) {
337
-            $padlen = 4 - $remainder;
338
-            $input .= \str_repeat('=', $padlen);
339
-        }
340
-        return \base64_decode(\strtr($input, '-_', '+/'));
341
-    }
342
-
343
-    /**
344
-     * Encode a string with URL-safe Base64.
345
-     *
346
-     * @param string $input The string you want encoded
347
-     *
348
-     * @return string The base64 encode of what you passed in
349
-     */
350
-    public static function urlsafeB64Encode($input)
351
-    {
352
-        return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
353
-    }
354
-
355
-    /**
356
-     * Helper method to create a JSON error.
357
-     *
358
-     * @param int $errno An error number from json_last_error()
359
-     *
360
-     * @return void
361
-     */
362
-    private static function handleJsonError($errno)
363
-    {
364
-        $messages = array(
365
-            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
366
-            JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
367
-            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
368
-            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
369
-            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
370
-        );
371
-        throw new DomainException(
372
-            isset($messages[$errno])
373
-            ? $messages[$errno]
374
-            : 'Unknown JSON error: ' . $errno
375
-        );
376
-    }
377
-
378
-    /**
379
-     * Get the number of bytes in cryptographic strings.
380
-     *
381
-     * @param string $str
382
-     *
383
-     * @return int
384
-     */
385
-    private static function safeStrlen($str)
386
-    {
387
-        if (\function_exists('mb_strlen')) {
388
-            return \mb_strlen($str, '8bit');
389
-        }
390
-        return \strlen($str);
391
-    }
392
-
393
-    /**
394
-     * Convert an ECDSA signature to an ASN.1 DER sequence
395
-     *
396
-     * @param   string $sig The ECDSA signature to convert
397
-     * @return  string The encoded DER object
398
-     */
399
-    private static function signatureToDER($sig)
400
-    {
401
-        // Separate the signature into r-value and s-value
402
-        list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
403
-
404
-        // Trim leading zeros
405
-        $r = \ltrim($r, "\x00");
406
-        $s = \ltrim($s, "\x00");
407
-
408
-        // Convert r-value and s-value from unsigned big-endian integers to
409
-        // signed two's complement
410
-        if (\ord($r[0]) > 0x7f) {
411
-            $r = "\x00" . $r;
412
-        }
413
-        if (\ord($s[0]) > 0x7f) {
414
-            $s = "\x00" . $s;
415
-        }
416
-
417
-        return self::encodeDER(
418
-            self::ASN1_SEQUENCE,
419
-            self::encodeDER(self::ASN1_INTEGER, $r) .
420
-            self::encodeDER(self::ASN1_INTEGER, $s)
421
-        );
422
-    }
423
-
424
-    /**
425
-     * Encodes a value into a DER object.
426
-     *
427
-     * @param   int     $type DER tag
428
-     * @param   string  $value the value to encode
429
-     * @return  string  the encoded object
430
-     */
431
-    private static function encodeDER($type, $value)
432
-    {
433
-        $tag_header = 0;
434
-        if ($type === self::ASN1_SEQUENCE) {
435
-            $tag_header |= 0x20;
436
-        }
437
-
438
-        // Type
439
-        $der = \chr($tag_header | $type);
440
-
441
-        // Length
442
-        $der .= \chr(\strlen($value));
443
-
444
-        return $der . $value;
445
-    }
446
-
447
-    /**
448
-     * Encodes signature from a DER object.
449
-     *
450
-     * @param   string  $der binary signature in DER format
451
-     * @param   int     $keySize the number of bits in the key
452
-     * @return  string  the signature
453
-     */
454
-    private static function signatureFromDER($der, $keySize)
455
-    {
456
-        // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
457
-        list($offset, $_) = self::readDER($der);
458
-        list($offset, $r) = self::readDER($der, $offset);
459
-        list($offset, $s) = self::readDER($der, $offset);
460
-
461
-        // Convert r-value and s-value from signed two's compliment to unsigned
462
-        // big-endian integers
463
-        $r = \ltrim($r, "\x00");
464
-        $s = \ltrim($s, "\x00");
465
-
466
-        // Pad out r and s so that they are $keySize bits long
467
-        $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
468
-        $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
469
-
470
-        return $r . $s;
471
-    }
472
-
473
-    /**
474
-     * Reads binary DER-encoded data and decodes into a single object
475
-     *
476
-     * @param string $der the binary data in DER format
477
-     * @param int $offset the offset of the data stream containing the object
478
-     * to decode
479
-     * @return array [$offset, $data] the new offset and the decoded object
480
-     */
481
-    private static function readDER($der, $offset = 0)
482
-    {
483
-        $pos = $offset;
484
-        $size = \strlen($der);
485
-        $constructed = (\ord($der[$pos]) >> 5) & 0x01;
486
-        $type = \ord($der[$pos++]) & 0x1f;
487
-
488
-        // Length
489
-        $len = \ord($der[$pos++]);
490
-        if ($len & 0x80) {
491
-            $n = $len & 0x1f;
492
-            $len = 0;
493
-            while ($n-- && $pos < $size) {
494
-                $len = ($len << 8) | \ord($der[$pos++]);
495
-            }
496
-        }
497
-
498
-        // Value
499
-        if ($type == self::ASN1_BIT_STRING) {
500
-            $pos++; // Skip the first contents octet (padding indicator)
501
-            $data = \substr($der, $pos, $len - 1);
502
-            $pos += $len - 1;
503
-        } elseif (!$constructed) {
504
-            $data = \substr($der, $pos, $len);
505
-            $pos += $len;
506
-        } else {
507
-            $data = null;
508
-        }
509
-
510
-        return array($pos, $data);
511
-    }
512
-}
Browse code

Created repository.

DoubleBastionAdmin authored on 01/03/2022 23:47:00
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,512 @@
1
+<?php
2
+
3
+namespace Firebase\JWT;
4
+
5
+use \DomainException;
6
+use \InvalidArgumentException;
7
+use \UnexpectedValueException;
8
+use \DateTime;
9
+
10
+/**
11
+ * JSON Web Token implementation, based on this spec:
12
+ * https://tools.ietf.org/html/rfc7519
13
+ *
14
+ * PHP version 5
15
+ *
16
+ * @category Authentication
17
+ * @package  Authentication_JWT
18
+ * @author   Neuman Vong <neuman@twilio.com>
19
+ * @author   Anant Narayanan <anant@php.net>
20
+ * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
21
+ * @link     https://github.com/firebase/php-jwt
22
+ */
23
+class JWT
24
+{
25
+    const ASN1_INTEGER = 0x02;
26
+    const ASN1_SEQUENCE = 0x10;
27
+    const ASN1_BIT_STRING = 0x03;
28
+
29
+    /**
30
+     * When checking nbf, iat or expiration times,
31
+     * we want to provide some extra leeway time to
32
+     * account for clock skew.
33
+     */
34
+    public static $leeway = 0;
35
+
36
+    /**
37
+     * Allow the current timestamp to be specified.
38
+     * Useful for fixing a value within unit testing.
39
+     *
40
+     * Will default to PHP time() value if null.
41
+     */
42
+    public static $timestamp = null;
43
+
44
+    public static $supported_algs = array(
45
+        'ES256' => array('openssl', 'SHA256'),
46
+        'HS256' => array('hash_hmac', 'SHA256'),
47
+        'HS384' => array('hash_hmac', 'SHA384'),
48
+        'HS512' => array('hash_hmac', 'SHA512'),
49
+        'RS256' => array('openssl', 'SHA256'),
50
+        'RS384' => array('openssl', 'SHA384'),
51
+        'RS512' => array('openssl', 'SHA512'),
52
+    );
53
+
54
+    /**
55
+     * Decodes a JWT string into a PHP object.
56
+     *
57
+     * @param string                    $jwt            The JWT
58
+     * @param string|array|resource     $key            The key, or map of keys.
59
+     *                                                  If the algorithm used is asymmetric, this is the public key
60
+     * @param array                     $allowed_algs   List of supported verification algorithms
61
+     *                                                  Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
62
+     *
63
+     * @return object The JWT's payload as a PHP object
64
+     *
65
+     * @throws UnexpectedValueException     Provided JWT was invalid
66
+     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
67
+     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
68
+     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
69
+     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
70
+     *
71
+     * @uses jsonDecode
72
+     * @uses urlsafeB64Decode
73
+     */
74
+    public static function decode($jwt, $key, array $allowed_algs = array())
75
+    {
76
+        $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
77
+
78
+        if (empty($key)) {
79
+            throw new InvalidArgumentException('Key may not be empty');
80
+        }
81
+        $tks = \explode('.', $jwt);
82
+        if (\count($tks) != 3) {
83
+            throw new UnexpectedValueException('Wrong number of segments');
84
+        }
85
+        list($headb64, $bodyb64, $cryptob64) = $tks;
86
+        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
87
+            throw new UnexpectedValueException('Invalid header encoding');
88
+        }
89
+        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
90
+            throw new UnexpectedValueException('Invalid claims encoding');
91
+        }
92
+        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
93
+            throw new UnexpectedValueException('Invalid signature encoding');
94
+        }
95
+        if (empty($header->alg)) {
96
+            throw new UnexpectedValueException('Empty algorithm');
97
+        }
98
+        if (empty(static::$supported_algs[$header->alg])) {
99
+            throw new UnexpectedValueException('Algorithm not supported');
100
+        }
101
+        if (!\in_array($header->alg, $allowed_algs)) {
102
+            throw new UnexpectedValueException('Algorithm not allowed');
103
+        }
104
+        if ($header->alg === 'ES256') {
105
+            // OpenSSL expects an ASN.1 DER sequence for ES256 signatures
106
+            $sig = self::signatureToDER($sig);
107
+        }
108
+
109
+        if (\is_array($key) || $key instanceof \ArrayAccess) {
110
+            if (isset($header->kid)) {
111
+                if (!isset($key[$header->kid])) {
112
+                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
113
+                }
114
+                $key = $key[$header->kid];
115
+            } else {
116
+                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
117
+            }
118
+        }
119
+
120
+        // Check the signature
121
+        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
122
+            throw new SignatureInvalidException('Signature verification failed');
123
+        }
124
+
125
+        // Check the nbf if it is defined. This is the time that the
126
+        // token can actually be used. If it's not yet that time, abort.
127
+        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
128
+            throw new BeforeValidException(
129
+                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
130
+            );
131
+        }
132
+
133
+        // Check that this token has been created before 'now'. This prevents
134
+        // using tokens that have been created for later use (and haven't
135
+        // correctly used the nbf claim).
136
+        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
137
+            throw new BeforeValidException(
138
+                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
139
+            );
140
+        }
141
+
142
+        // Check if this token has expired.
143
+        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
144
+            throw new ExpiredException('Expired token');
145
+        }
146
+
147
+        return $payload;
148
+    }
149
+
150
+    /**
151
+     * Converts and signs a PHP object or array into a JWT string.
152
+     *
153
+     * @param object|array  $payload    PHP object or array
154
+     * @param string        $key        The secret key.
155
+     *                                  If the algorithm used is asymmetric, this is the private key
156
+     * @param string        $alg        The signing algorithm.
157
+     *                                  Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
158
+     * @param mixed         $keyId
159
+     * @param array         $head       An array with header elements to attach
160
+     *
161
+     * @return string A signed JWT
162
+     *
163
+     * @uses jsonEncode
164
+     * @uses urlsafeB64Encode
165
+     */
166
+    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
167
+    {
168
+        $header = array('typ' => 'JWT', 'alg' => $alg);
169
+        if ($keyId !== null) {
170
+            $header['kid'] = $keyId;
171
+        }
172
+        if (isset($head) && \is_array($head)) {
173
+            $header = \array_merge($head, $header);
174
+        }
175
+        $segments = array();
176
+        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
177
+        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
178
+        $signing_input = \implode('.', $segments);
179
+
180
+        $signature = static::sign($signing_input, $key, $alg);
181
+        $segments[] = static::urlsafeB64Encode($signature);
182
+
183
+        return \implode('.', $segments);
184
+    }
185
+
186
+    /**
187
+     * Sign a string with a given key and algorithm.
188
+     *
189
+     * @param string            $msg    The message to sign
190
+     * @param string|resource   $key    The secret key
191
+     * @param string            $alg    The signing algorithm.
192
+     *                                  Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
193
+     *
194
+     * @return string An encrypted message
195
+     *
196
+     * @throws DomainException Unsupported algorithm was specified
197
+     */
198
+    public static function sign($msg, $key, $alg = 'HS256')
199
+    {
200
+        if (empty(static::$supported_algs[$alg])) {
201
+            throw new DomainException('Algorithm not supported');
202
+        }
203
+        list($function, $algorithm) = static::$supported_algs[$alg];
204
+        switch ($function) {
205
+            case 'hash_hmac':
206
+                return \hash_hmac($algorithm, $msg, $key, true);
207
+            case 'openssl':
208
+                $signature = '';
209
+                $success = \openssl_sign($msg, $signature, $key, $algorithm);
210
+                if (!$success) {
211
+                    throw new DomainException("OpenSSL unable to sign data");
212
+                } else {
213
+                    if ($alg === 'ES256') {
214
+                        $signature = self::signatureFromDER($signature, 256);
215
+                    }
216
+                    return $signature;
217
+                }
218
+        }
219
+    }
220
+
221
+    /**
222
+     * Verify a signature with the message, key and method. Not all methods
223
+     * are symmetric, so we must have a separate verify and sign method.
224
+     *
225
+     * @param string            $msg        The original message (header and body)
226
+     * @param string            $signature  The original signature
227
+     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
228
+     * @param string            $alg        The algorithm
229
+     *
230
+     * @return bool
231
+     *
232
+     * @throws DomainException Invalid Algorithm or OpenSSL failure
233
+     */
234
+    private static function verify($msg, $signature, $key, $alg)
235
+    {
236
+        if (empty(static::$supported_algs[$alg])) {
237
+            throw new DomainException('Algorithm not supported');
238
+        }
239
+
240
+        list($function, $algorithm) = static::$supported_algs[$alg];
241
+        switch ($function) {
242
+            case 'openssl':
243
+                $success = \openssl_verify($msg, $signature, $key, $algorithm);
244
+                if ($success === 1) {
245
+                    return true;
246
+                } elseif ($success === 0) {
247
+                    return false;
248
+                }
249
+                // returns 1 on success, 0 on failure, -1 on error.
250
+                throw new DomainException(
251
+                    'OpenSSL error: ' . \openssl_error_string()
252
+                );
253
+            case 'hash_hmac':
254
+            default:
255
+                $hash = \hash_hmac($algorithm, $msg, $key, true);
256
+                if (\function_exists('hash_equals')) {
257
+                    return \hash_equals($signature, $hash);
258
+                }
259
+                $len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
260
+
261
+                $status = 0;
262
+                for ($i = 0; $i < $len; $i++) {
263
+                    $status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
264
+                }
265
+                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
266
+
267
+                return ($status === 0);
268
+        }
269
+    }
270
+
271
+    /**
272
+     * Decode a JSON string into a PHP object.
273
+     *
274
+     * @param string $input JSON string
275
+     *
276
+     * @return object Object representation of JSON string
277
+     *
278
+     * @throws DomainException Provided string was invalid JSON
279
+     */
280
+    public static function jsonDecode($input)
281
+    {
282
+        if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
283
+            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
284
+             * to specify that large ints (like Steam Transaction IDs) should be treated as
285
+             * strings, rather than the PHP default behaviour of converting them to floats.
286
+             */
287
+            $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
288
+        } else {
289
+            /** Not all servers will support that, however, so for older versions we must
290
+             * manually detect large ints in the JSON string and quote them (thus converting
291
+             *them to strings) before decoding, hence the preg_replace() call.
292
+             */
293
+            $max_int_length = \strlen((string) PHP_INT_MAX) - 1;
294
+            $json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
295
+            $obj = \json_decode($json_without_bigints);
296
+        }
297
+
298
+        if ($errno = \json_last_error()) {
299
+            static::handleJsonError($errno);
300
+        } elseif ($obj === null && $input !== 'null') {
301
+            throw new DomainException('Null result with non-null input');
302
+        }
303
+        return $obj;
304
+    }
305
+
306
+    /**
307
+     * Encode a PHP object into a JSON string.
308
+     *
309
+     * @param object|array $input A PHP object or array
310
+     *
311
+     * @return string JSON representation of the PHP object or array
312
+     *
313
+     * @throws DomainException Provided object could not be encoded to valid JSON
314
+     */
315
+    public static function jsonEncode($input)
316
+    {
317
+        $json = \json_encode($input);
318
+        if ($errno = \json_last_error()) {
319
+            static::handleJsonError($errno);
320
+        } elseif ($json === 'null' && $input !== null) {
321
+            throw new DomainException('Null result with non-null input');
322
+        }
323
+        return $json;
324
+    }
325
+
326
+    /**
327
+     * Decode a string with URL-safe Base64.
328
+     *
329
+     * @param string $input A Base64 encoded string
330
+     *
331
+     * @return string A decoded string
332
+     */
333
+    public static function urlsafeB64Decode($input)
334
+    {
335
+        $remainder = \strlen($input) % 4;
336
+        if ($remainder) {
337
+            $padlen = 4 - $remainder;
338
+            $input .= \str_repeat('=', $padlen);
339
+        }
340
+        return \base64_decode(\strtr($input, '-_', '+/'));
341
+    }
342
+
343
+    /**
344
+     * Encode a string with URL-safe Base64.
345
+     *
346
+     * @param string $input The string you want encoded
347
+     *
348
+     * @return string The base64 encode of what you passed in
349
+     */
350
+    public static function urlsafeB64Encode($input)
351
+    {
352
+        return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
353
+    }
354
+
355
+    /**
356
+     * Helper method to create a JSON error.
357
+     *
358
+     * @param int $errno An error number from json_last_error()
359
+     *
360
+     * @return void
361
+     */
362
+    private static function handleJsonError($errno)
363
+    {
364
+        $messages = array(
365
+            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
366
+            JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
367
+            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
368
+            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
369
+            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
370
+        );
371
+        throw new DomainException(
372
+            isset($messages[$errno])
373
+            ? $messages[$errno]
374
+            : 'Unknown JSON error: ' . $errno
375
+        );
376
+    }
377
+
378
+    /**
379
+     * Get the number of bytes in cryptographic strings.
380
+     *
381
+     * @param string $str
382
+     *
383
+     * @return int
384
+     */
385
+    private static function safeStrlen($str)
386
+    {
387
+        if (\function_exists('mb_strlen')) {
388
+            return \mb_strlen($str, '8bit');
389
+        }
390
+        return \strlen($str);
391
+    }
392
+
393
+    /**
394
+     * Convert an ECDSA signature to an ASN.1 DER sequence
395
+     *
396
+     * @param   string $sig The ECDSA signature to convert
397
+     * @return  string The encoded DER object
398
+     */
399
+    private static function signatureToDER($sig)
400
+    {
401
+        // Separate the signature into r-value and s-value
402
+        list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
403
+
404
+        // Trim leading zeros
405
+        $r = \ltrim($r, "\x00");
406
+        $s = \ltrim($s, "\x00");
407
+
408
+        // Convert r-value and s-value from unsigned big-endian integers to
409
+        // signed two's complement
410
+        if (\ord($r[0]) > 0x7f) {
411
+            $r = "\x00" . $r;
412
+        }
413
+        if (\ord($s[0]) > 0x7f) {
414
+            $s = "\x00" . $s;
415
+        }
416
+
417
+        return self::encodeDER(
418
+            self::ASN1_SEQUENCE,
419
+            self::encodeDER(self::ASN1_INTEGER, $r) .
420
+            self::encodeDER(self::ASN1_INTEGER, $s)
421
+        );
422
+    }
423
+
424
+    /**
425
+     * Encodes a value into a DER object.
426
+     *
427
+     * @param   int     $type DER tag
428
+     * @param   string  $value the value to encode
429
+     * @return  string  the encoded object
430
+     */
431
+    private static function encodeDER($type, $value)
432
+    {
433
+        $tag_header = 0;
434
+        if ($type === self::ASN1_SEQUENCE) {
435
+            $tag_header |= 0x20;
436
+        }
437
+
438
+        // Type
439
+        $der = \chr($tag_header | $type);
440
+
441
+        // Length
442
+        $der .= \chr(\strlen($value));
443
+
444
+        return $der . $value;
445
+    }
446
+
447
+    /**
448
+     * Encodes signature from a DER object.
449
+     *
450
+     * @param   string  $der binary signature in DER format
451
+     * @param   int     $keySize the number of bits in the key
452
+     * @return  string  the signature
453
+     */
454
+    private static function signatureFromDER($der, $keySize)
455
+    {
456
+        // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
457
+        list($offset, $_) = self::readDER($der);
458
+        list($offset, $r) = self::readDER($der, $offset);
459
+        list($offset, $s) = self::readDER($der, $offset);
460
+
461
+        // Convert r-value and s-value from signed two's compliment to unsigned
462
+        // big-endian integers
463
+        $r = \ltrim($r, "\x00");
464
+        $s = \ltrim($s, "\x00");
465
+
466
+        // Pad out r and s so that they are $keySize bits long
467
+        $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
468
+        $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
469
+
470
+        return $r . $s;
471
+    }
472
+
473
+    /**
474
+     * Reads binary DER-encoded data and decodes into a single object
475
+     *
476
+     * @param string $der the binary data in DER format
477
+     * @param int $offset the offset of the data stream containing the object
478
+     * to decode
479
+     * @return array [$offset, $data] the new offset and the decoded object
480
+     */
481
+    private static function readDER($der, $offset = 0)
482
+    {
483
+        $pos = $offset;
484
+        $size = \strlen($der);
485
+        $constructed = (\ord($der[$pos]) >> 5) & 0x01;
486
+        $type = \ord($der[$pos++]) & 0x1f;
487
+
488
+        // Length
489
+        $len = \ord($der[$pos++]);
490
+        if ($len & 0x80) {
491
+            $n = $len & 0x1f;
492
+            $len = 0;
493
+            while ($n-- && $pos < $size) {
494
+                $len = ($len << 8) | \ord($der[$pos++]);
495
+            }
496
+        }
497
+
498
+        // Value
499
+        if ($type == self::ASN1_BIT_STRING) {
500
+            $pos++; // Skip the first contents octet (padding indicator)
501
+            $data = \substr($der, $pos, $len - 1);
502
+            $pos += $len - 1;
503
+        } elseif (!$constructed) {
504
+            $data = \substr($der, $pos, $len);
505
+            $pos += $len;
506
+        } else {
507
+            $data = null;
508
+        }
509
+
510
+        return array($pos, $data);
511
+    }
512
+}