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.
Zur Liste
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 +