PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / firebase / php-jwt / src / JWK.php
ameliabooking / vendor / firebase / php-jwt / src Last commit date
BeforeValidException.php 6 months ago CachedKeySet.php 6 months ago ExpiredException.php 6 months ago JWK.php 6 months ago JWT.php 6 months ago JWTExceptionWithPayloadInterface.php 6 months ago Key.php 6 months ago SignatureInvalidException.php 6 months ago
JWK.php
350 lines
1 <?php
2
3 namespace AmeliaVendor\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