Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor-pro/vendor/firebase/php-jwt/src/JWK.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
3
+
namespace Firebase\JWT;
4
+
5
+
use DomainException;
6
+
use InvalidArgumentException;
7
+
use UnexpectedValueException;
8
+
9
+
/**
10
+
* JSON Web Key implementation, based on this spec:
11
+
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
12
+
*
13
+
* PHP version 5
14
+
*
15
+
* @category Authentication
16
+
* @package Authentication_JWT
17
+
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
18
+
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
19
+
* @link https://github.com/firebase/php-jwt
20
+
*/
21
+
class JWK
22
+
{
23
+
private const OID = '1.2.840.10045.2.1';
24
+
private const ASN1_OBJECT_IDENTIFIER = 0x06;
25
+
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
26
+
private const ASN1_BIT_STRING = 0x03;
27
+
private const EC_CURVES = [
28
+
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
29
+
'secp256k1' => '1.3.132.0.10', // Len: 64
30
+
'P-384' => '1.3.132.0.34', // Len: 96
31
+
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
32
+
];
33
+
34
+
// For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype.
35
+
// This library supports the following subtypes:
36
+
private const OKP_SUBTYPES = [
37
+
'Ed25519' => true, // RFC 8037
38
+
];
39
+
40
+
/**
41
+
* Parse a set of JWK keys
42
+
*
43
+
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
44
+
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
45
+
* JSON Web Key Set
46
+
*
47
+
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
48
+
*
49
+
* @throws InvalidArgumentException Provided JWK Set is empty
50
+
* @throws UnexpectedValueException Provided JWK Set was invalid
51
+
* @throws DomainException OpenSSL failure
52
+
*
53
+
* @uses parseKey
54
+
*/
55
+
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
56
+
{
57
+
$keys = [];
58
+
59
+
if (!isset($jwks['keys'])) {
60
+
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
61
+
}
62
+
63
+
if (empty($jwks['keys'])) {
64
+
throw new InvalidArgumentException('JWK Set did not contain any keys');
65
+
}
66
+
67
+
foreach ($jwks['keys'] as $k => $v) {
68
+
$kid = isset($v['kid']) ? $v['kid'] : $k;
69
+
if ($key = self::parseKey($v, $defaultAlg)) {
70
+
$keys[(string) $kid] = $key;
71
+
}
72
+
}
73
+
74
+
if (0 === \count($keys)) {
75
+
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
76
+
}
77
+
78
+
return $keys;
79
+
}
80
+
81
+
/**
82
+
* Parse a JWK key
83
+
*
84
+
* @param array<mixed> $jwk An individual JWK
85
+
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
86
+
* JSON Web Key Set
87
+
*
88
+
* @return Key The key object for the JWK
89
+
*
90
+
* @throws InvalidArgumentException Provided JWK is empty
91
+
* @throws UnexpectedValueException Provided JWK was invalid
92
+
* @throws DomainException OpenSSL failure
93
+
*
94
+
* @uses createPemFromModulusAndExponent
95
+
*/
96
+
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
97
+
{
98
+
if (empty($jwk)) {
99
+
throw new InvalidArgumentException('JWK must not be empty');
100
+
}
101
+
102
+
if (!isset($jwk['kty'])) {
103
+
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
104
+
}
105
+
106
+
if (!isset($jwk['alg'])) {
107
+
if (\is_null($defaultAlg)) {
108
+
// The "alg" parameter is optional in a KTY, but an algorithm is required
109
+
// for parsing in this library. Use the $defaultAlg parameter when parsing the
110
+
// key set in order to prevent this error.
111
+
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
112
+
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
113
+
}
114
+
$jwk['alg'] = $defaultAlg;
115
+
}
116
+
117
+
switch ($jwk['kty']) {
118
+
case 'RSA':
119
+
if (!empty($jwk['d'])) {
120
+
throw new UnexpectedValueException('RSA private keys are not supported');
121
+
}
122
+
if (!isset($jwk['n']) || !isset($jwk['e'])) {
123
+
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
124
+
}
125
+
126
+
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
127
+
$publicKey = \openssl_pkey_get_public($pem);
128
+
if (false === $publicKey) {
129
+
throw new DomainException(
130
+
'OpenSSL error: ' . \openssl_error_string()
131
+
);
132
+
}
133
+
return new Key($publicKey, $jwk['alg']);
134
+
case 'EC':
135
+
if (isset($jwk['d'])) {
136
+
// The key is actually a private key
137
+
throw new UnexpectedValueException('Key data must be for a public key');
138
+
}
139
+
140
+
if (empty($jwk['crv'])) {
141
+
throw new UnexpectedValueException('crv not set');
142
+
}
143
+
144
+
if (!isset(self::EC_CURVES[$jwk['crv']])) {
145
+
throw new DomainException('Unrecognised or unsupported EC curve');
146
+
}
147
+
148
+
if (empty($jwk['x']) || empty($jwk['y'])) {
149
+
throw new UnexpectedValueException('x and y not set');
150
+
}
151
+
152
+
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
153
+
return new Key($publicKey, $jwk['alg']);
154
+
case 'OKP':
155
+
if (isset($jwk['d'])) {
156
+
// The key is actually a private key
157
+
throw new UnexpectedValueException('Key data must be for a public key');
158
+
}
159
+
160
+
if (!isset($jwk['crv'])) {
161
+
throw new UnexpectedValueException('crv not set');
162
+
}
163
+
164
+
if (empty(self::OKP_SUBTYPES[$jwk['crv']])) {
165
+
throw new DomainException('Unrecognised or unsupported OKP key subtype');
166
+
}
167
+
168
+
if (empty($jwk['x'])) {
169
+
throw new UnexpectedValueException('x not set');
170
+
}
171
+
172
+
// This library works internally with EdDSA keys (Ed25519) encoded in standard base64.
173
+
$publicKey = JWT::convertBase64urlToBase64($jwk['x']);
174
+
return new Key($publicKey, $jwk['alg']);
175
+
default:
176
+
break;
177
+
}
178
+
179
+
return null;
180
+
}
181
+
182
+
/**
183
+
* Converts the EC JWK values to pem format.
184
+
*
185
+
* @param string $crv The EC curve (only P-256 & P-384 is supported)
186
+
* @param string $x The EC x-coordinate
187
+
* @param string $y The EC y-coordinate
188
+
*
189
+
* @return string
190
+
*/
191
+
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
192
+
{
193
+
$pem =
194
+
self::encodeDER(
195
+
self::ASN1_SEQUENCE,
196
+
self::encodeDER(
197
+
self::ASN1_SEQUENCE,
198
+
self::encodeDER(
199
+
self::ASN1_OBJECT_IDENTIFIER,
200
+
self::encodeOID(self::OID)
201
+
)
202
+
. self::encodeDER(
203
+
self::ASN1_OBJECT_IDENTIFIER,
204
+
self::encodeOID(self::EC_CURVES[$crv])
205
+
)
206
+
) .
207
+
self::encodeDER(
208
+
self::ASN1_BIT_STRING,
209
+
\chr(0x00) . \chr(0x04)
210
+
. JWT::urlsafeB64Decode($x)
211
+
. JWT::urlsafeB64Decode($y)
212
+
)
213
+
);
214
+
215
+
return sprintf(
216
+
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
217
+
wordwrap(base64_encode($pem), 64, "\n", true)
218
+
);
219
+
}
220
+
221
+
/**
222
+
* Create a public key represented in PEM format from RSA modulus and exponent information
223
+
*
224
+
* @param string $n The RSA modulus encoded in Base64
225
+
* @param string $e The RSA exponent encoded in Base64
226
+
*
227
+
* @return string The RSA public key represented in PEM format
228
+
*
229
+
* @uses encodeLength
230
+
*/
231
+
private static function createPemFromModulusAndExponent(
232
+
string $n,
233
+
string $e
234
+
): string {
235
+
$mod = JWT::urlsafeB64Decode($n);
236
+
$exp = JWT::urlsafeB64Decode($e);
237
+
238
+
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
239
+
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
240
+
241
+
$rsaPublicKey = \pack(
242
+
'Ca*a*a*',
243
+
48,
244
+
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
245
+
$modulus,
246
+
$publicExponent
247
+
);
248
+
249
+
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
250
+
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
251
+
$rsaPublicKey = \chr(0) . $rsaPublicKey;
252
+
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
253
+
254
+
$rsaPublicKey = \pack(
255
+
'Ca*a*',
256
+
48,
257
+
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
258
+
$rsaOID . $rsaPublicKey
259
+
);
260
+
261
+
return "-----BEGIN PUBLIC KEY-----\r\n" .
262
+
\chunk_split(\base64_encode($rsaPublicKey), 64) .
263
+
'-----END PUBLIC KEY-----';
264
+
}
265
+
266
+
/**
267
+
* DER-encode the length
268
+
*
269
+
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
270
+
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
271
+
*
272
+
* @param int $length
273
+
* @return string
274
+
*/
275
+
private static function encodeLength(int $length): string
276
+
{
277
+
if ($length <= 0x7F) {
278
+
return \chr($length);
279
+
}
280
+
281
+
$temp = \ltrim(\pack('N', $length), \chr(0));
282
+
283
+
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
284
+
}
285
+
286
+
/**
287
+
* Encodes a value into a DER object.
288
+
* Also defined in Firebase\JWT\JWT
289
+
*
290
+
* @param int $type DER tag
291
+
* @param string $value the value to encode
292
+
* @return string the encoded object
293
+
*/
294
+
private static function encodeDER(int $type, string $value): string
295
+
{
296
+
$tag_header = 0;
297
+
if ($type === self::ASN1_SEQUENCE) {
298
+
$tag_header |= 0x20;
299
+
}
300
+
301
+
// Type
302
+
$der = \chr($tag_header | $type);
303
+
304
+
// Length
305
+
$der .= \chr(\strlen($value));
306
+
307
+
return $der . $value;
308
+
}
309
+
310
+
/**
311
+
* Encodes a string into a DER-encoded OID.
312
+
*
313
+
* @param string $oid the OID string
314
+
* @return string the binary DER-encoded OID
315
+
*/
316
+
private static function encodeOID(string $oid): string
317
+
{
318
+
$octets = explode('.', $oid);
319
+
320
+
// Get the first octet
321
+
$first = (int) array_shift($octets);
322
+
$second = (int) array_shift($octets);
323
+
$oid = \chr($first * 40 + $second);
324
+
325
+
// Iterate over subsequent octets
326
+
foreach ($octets as $octet) {
327
+
if ($octet == 0) {
328
+
$oid .= \chr(0x00);
329
+
continue;
330
+
}
331
+
$bin = '';
332
+
333
+
while ($octet) {
334
+
$bin .= \chr(0x80 | ($octet & 0x7f));
335
+
$octet >>= 7;
336
+
}
337
+
$bin[0] = $bin[0] & \chr(0x7f);
338
+
339
+
// Convert to big endian if necessary
340
+
if (pack('V', 65534) == pack('L', 65534)) {
341
+
$oid .= strrev($bin);
342
+
} else {
343
+
$oid .= $bin;
344
+
}
345
+
}
346
+
347
+
return $oid;
348
+
}
349
+
}
350
+