Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/paid-memberships-pro/includes/lib/php-jwt/JWT.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
// Added PMPro to namespace to avoid conflicts.
3
+
namespace PMPro\Firebase\JWT;
4
+
use \DomainException;
5
+
use \InvalidArgumentException;
6
+
use \UnexpectedValueException;
7
+
use \DateTime;
8
+
9
+
/**
10
+
* JSON Web Token implementation, based on this spec:
11
+
* https://tools.ietf.org/html/rfc7519
12
+
*
13
+
* PHP version 5
14
+
*
15
+
* @category Authentication
16
+
* @package Authentication_JWT
17
+
* @author Neuman Vong <neuman@twilio.com>
18
+
* @author Anant Narayanan <anant@php.net>
19
+
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
20
+
* @link https://github.com/firebase/php-jwt
21
+
*/
22
+
class JWT
23
+
{
24
+
25
+
/**
26
+
* When checking nbf, iat or expiration times,
27
+
* we want to provide some extra leeway time to
28
+
* account for clock skew.
29
+
*/
30
+
public static $leeway = 0;
31
+
32
+
/**
33
+
* Allow the current timestamp to be specified.
34
+
* Useful for fixing a value within unit testing.
35
+
*
36
+
* Will default to PHP time() value if null.
37
+
*/
38
+
public static $timestamp = null;
39
+
40
+
public static $supported_algs = array(
41
+
'HS256' => array('hash_hmac', 'SHA256'),
42
+
'HS512' => array('hash_hmac', 'SHA512'),
43
+
'HS384' => array('hash_hmac', 'SHA384'),
44
+
'RS256' => array('openssl', 'SHA256'),
45
+
'RS384' => array('openssl', 'SHA384'),
46
+
'RS512' => array('openssl', 'SHA512'),
47
+
);
48
+
49
+
/**
50
+
* Decodes a JWT string into a PHP object.
51
+
*
52
+
* @param string $jwt The JWT
53
+
* @param string|array $key The key, or map of keys.
54
+
* If the algorithm used is asymmetric, this is the public key
55
+
* @param array $allowed_algs List of supported verification algorithms
56
+
* Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
57
+
*
58
+
* @return object The JWT's payload as a PHP object
59
+
*
60
+
* @throws UnexpectedValueException Provided JWT was invalid
61
+
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
62
+
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
63
+
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
64
+
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
65
+
*
66
+
* @uses jsonDecode
67
+
* @uses urlsafeB64Decode
68
+
*/
69
+
public static function decode($jwt, $key, array $allowed_algs = array())
70
+
{
71
+
$timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
72
+
73
+
if (empty($key)) {
74
+
throw new InvalidArgumentException('Key may not be empty');
75
+
}
76
+
$tks = explode('.', $jwt);
77
+
if (count($tks) != 3) {
78
+
throw new UnexpectedValueException('Wrong number of segments');
79
+
}
80
+
list($headb64, $bodyb64, $cryptob64) = $tks;
81
+
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
82
+
throw new UnexpectedValueException('Invalid header encoding');
83
+
}
84
+
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
85
+
throw new UnexpectedValueException('Invalid claims encoding');
86
+
}
87
+
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
88
+
throw new UnexpectedValueException('Invalid signature encoding');
89
+
}
90
+
if (empty($header->alg)) {
91
+
throw new UnexpectedValueException('Empty algorithm');
92
+
}
93
+
if (empty(static::$supported_algs[$header->alg])) {
94
+
throw new UnexpectedValueException('Algorithm not supported');
95
+
}
96
+
if (!in_array($header->alg, $allowed_algs)) {
97
+
throw new UnexpectedValueException('Algorithm not allowed');
98
+
}
99
+
if (is_array($key) || $key instanceof \ArrayAccess) {
100
+
if (isset($header->kid)) {
101
+
if (!isset($key[$header->kid])) {
102
+
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
103
+
}
104
+
$key = $key[$header->kid];
105
+
} else {
106
+
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
107
+
}
108
+
}
109
+
110
+
// Check the signature
111
+
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
112
+
throw new SignatureInvalidException('Signature verification failed');
113
+
}
114
+
115
+
// Check if the nbf if it is defined. This is the time that the
116
+
// token can actually be used. If it's not yet that time, abort.
117
+
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
118
+
throw new BeforeValidException(
119
+
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
120
+
);
121
+
}
122
+
123
+
// Check that this token has been created before 'now'. This prevents
124
+
// using tokens that have been created for later use (and haven't
125
+
// correctly used the nbf claim).
126
+
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
127
+
throw new BeforeValidException(
128
+
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
129
+
);
130
+
}
131
+
132
+
// Check if this token has expired.
133
+
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
134
+
throw new ExpiredException('Expired token');
135
+
}
136
+
137
+
return $payload;
138
+
}
139
+
140
+
/**
141
+
* Converts and signs a PHP object or array into a JWT string.
142
+
*
143
+
* @param object|array $payload PHP object or array
144
+
* @param string $key The secret key.
145
+
* If the algorithm used is asymmetric, this is the private key
146
+
* @param string $alg The signing algorithm.
147
+
* Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
148
+
* @param mixed $keyId
149
+
* @param array $head An array with header elements to attach
150
+
*
151
+
* @return string A signed JWT
152
+
*
153
+
* @uses jsonEncode
154
+
* @uses urlsafeB64Encode
155
+
*/
156
+
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
157
+
{
158
+
$header = array('typ' => 'JWT', 'alg' => $alg);
159
+
if ($keyId !== null) {
160
+
$header['kid'] = $keyId;
161
+
}
162
+
if ( isset($head) && is_array($head) ) {
163
+
$header = array_merge($head, $header);
164
+
}
165
+
$segments = array();
166
+
$segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
167
+
$segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
168
+
$signing_input = implode('.', $segments);
169
+
170
+
$signature = static::sign($signing_input, $key, $alg);
171
+
$segments[] = static::urlsafeB64Encode($signature);
172
+
173
+
return implode('.', $segments);
174
+
}
175
+
176
+
/**
177
+
* Sign a string with a given key and algorithm.
178
+
*
179
+
* @param string $msg The message to sign
180
+
* @param string|resource $key The secret key
181
+
* @param string $alg The signing algorithm.
182
+
* Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
183
+
*
184
+
* @return string An encrypted message
185
+
*
186
+
* @throws DomainException Unsupported algorithm was specified
187
+
*/
188
+
public static function sign($msg, $key, $alg = 'HS256')
189
+
{
190
+
if (empty(static::$supported_algs[$alg])) {
191
+
throw new DomainException('Algorithm not supported');
192
+
}
193
+
list($function, $algorithm) = static::$supported_algs[$alg];
194
+
switch($function) {
195
+
case 'hash_hmac':
196
+
return hash_hmac($algorithm, $msg, $key, true);
197
+
case 'openssl':
198
+
$signature = '';
199
+
$success = openssl_sign($msg, $signature, $key, $algorithm);
200
+
if (!$success) {
201
+
throw new DomainException("OpenSSL unable to sign data");
202
+
} else {
203
+
return $signature;
204
+
}
205
+
}
206
+
}
207
+
208
+
/**
209
+
* Verify a signature with the message, key and method. Not all methods
210
+
* are symmetric, so we must have a separate verify and sign method.
211
+
*
212
+
* @param string $msg The original message (header and body)
213
+
* @param string $signature The original signature
214
+
* @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
215
+
* @param string $alg The algorithm
216
+
*
217
+
* @return bool
218
+
*
219
+
* @throws DomainException Invalid Algorithm or OpenSSL failure
220
+
*/
221
+
private static function verify($msg, $signature, $key, $alg)
222
+
{
223
+
if (empty(static::$supported_algs[$alg])) {
224
+
throw new DomainException('Algorithm not supported');
225
+
}
226
+
227
+
list($function, $algorithm) = static::$supported_algs[$alg];
228
+
switch($function) {
229
+
case 'openssl':
230
+
$success = openssl_verify($msg, $signature, $key, $algorithm);
231
+
if ($success === 1) {
232
+
return true;
233
+
} elseif ($success === 0) {
234
+
return false;
235
+
}
236
+
// returns 1 on success, 0 on failure, -1 on error.
237
+
throw new DomainException(
238
+
'OpenSSL error: ' . openssl_error_string()
239
+
);
240
+
case 'hash_hmac':
241
+
default:
242
+
$hash = hash_hmac($algorithm, $msg, $key, true);
243
+
if (function_exists('hash_equals')) {
244
+
return hash_equals($signature, $hash);
245
+
}
246
+
$len = min(static::safeStrlen($signature), static::safeStrlen($hash));
247
+
248
+
$status = 0;
249
+
for ($i = 0; $i < $len; $i++) {
250
+
$status |= (ord($signature[$i]) ^ ord($hash[$i]));
251
+
}
252
+
$status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
253
+
254
+
return ($status === 0);
255
+
}
256
+
}
257
+
258
+
/**
259
+
* Decode a JSON string into a PHP object.
260
+
*
261
+
* @param string $input JSON string
262
+
*
263
+
* @return object Object representation of JSON string
264
+
*
265
+
* @throws DomainException Provided string was invalid JSON
266
+
*/
267
+
public static function jsonDecode($input)
268
+
{
269
+
if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
270
+
/** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
271
+
* to specify that large ints (like Steam Transaction IDs) should be treated as
272
+
* strings, rather than the PHP default behaviour of converting them to floats.
273
+
*/
274
+
$obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
275
+
} else {
276
+
/** Not all servers will support that, however, so for older versions we must
277
+
* manually detect large ints in the JSON string and quote them (thus converting
278
+
*them to strings) before decoding, hence the preg_replace() call.
279
+
*/
280
+
$max_int_length = strlen((string) PHP_INT_MAX) - 1;
281
+
$json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
282
+
$obj = json_decode($json_without_bigints);
283
+
}
284
+
285
+
if (function_exists('json_last_error') && $errno = json_last_error()) {
286
+
static::handleJsonError($errno);
287
+
} elseif ($obj === null && $input !== 'null') {
288
+
throw new DomainException('Null result with non-null input');
289
+
}
290
+
return $obj;
291
+
}
292
+
293
+
/**
294
+
* Encode a PHP object into a JSON string.
295
+
*
296
+
* @param object|array $input A PHP object or array
297
+
*
298
+
* @return string JSON representation of the PHP object or array
299
+
*
300
+
* @throws DomainException Provided object could not be encoded to valid JSON
301
+
*/
302
+
public static function jsonEncode($input)
303
+
{
304
+
$json = json_encode($input);
305
+
if (function_exists('json_last_error') && $errno = json_last_error()) {
306
+
static::handleJsonError($errno);
307
+
} elseif ($json === 'null' && $input !== null) {
308
+
throw new DomainException('Null result with non-null input');
309
+
}
310
+
return $json;
311
+
}
312
+
313
+
/**
314
+
* Decode a string with URL-safe Base64.
315
+
*
316
+
* @param string $input A Base64 encoded string
317
+
*
318
+
* @return string A decoded string
319
+
*/
320
+
public static function urlsafeB64Decode($input)
321
+
{
322
+
$remainder = strlen($input) % 4;
323
+
if ($remainder) {
324
+
$padlen = 4 - $remainder;
325
+
$input .= str_repeat('=', $padlen);
326
+
}
327
+
return base64_decode(strtr($input, '-_', '+/'));
328
+
}
329
+
330
+
/**
331
+
* Encode a string with URL-safe Base64.
332
+
*
333
+
* @param string $input The string you want encoded
334
+
*
335
+
* @return string The base64 encode of what you passed in
336
+
*/
337
+
public static function urlsafeB64Encode($input)
338
+
{
339
+
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
340
+
}
341
+
342
+
/**
343
+
* Helper method to create a JSON error.
344
+
*
345
+
* @param int $errno An error number from json_last_error()
346
+
*
347
+
* @return void
348
+
*/
349
+
private static function handleJsonError($errno)
350
+
{
351
+
$messages = array(
352
+
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
353
+
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
354
+
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
355
+
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
356
+
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
357
+
);
358
+
throw new DomainException(
359
+
isset($messages[$errno])
360
+
? $messages[$errno]
361
+
: 'Unknown JSON error: ' . $errno
362
+
);
363
+
}
364
+
365
+
/**
366
+
* Get the number of bytes in cryptographic strings.
367
+
*
368
+
* @param string
369
+
*
370
+
* @return int
371
+
*/
372
+
private static function safeStrlen($str)
373
+
{
374
+
if (function_exists('mb_strlen')) {
375
+
return mb_strlen($str, '8bit');
376
+
}
377
+
return strlen($str);
378
+
}
379
+
}
380
+