PluginProbe
WP-Stateless – Google Cloud Storage / 3.2.2
WP-Stateless – Google Cloud Storage v3.2.2
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / vendor / firebase / php-jwt / src / JWK.php

JWK.php in WP-Stateless – Google Cloud Storage 3.2.2, at vendor/firebase/php-jwt/src/JWK.php

184 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 /**
24 * Parse a set of JWK keys
25 *
26 * @param array<mixed> $jwks The JSON Web Key Set as an associative array
27 *
28 * @return array<string, Key> An associative array of key IDs (kid) to Key objects
29 *
30 * @throws InvalidArgumentException Provided JWK Set is empty
31 * @throws UnexpectedValueException Provided JWK Set was invalid
32 * @throws DomainException OpenSSL failure
33 *
34 * @uses parseKey
35 */
36 public static function parseKeySet(array $jwks): array
37 {
38 $keys = [];
39
40 if (!isset($jwks['keys'])) {
41 throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
42 }
43
44 if (empty($jwks['keys'])) {
45 throw new InvalidArgumentException('JWK Set did not contain any keys');
46 }
47
48 foreach ($jwks['keys'] as $k => $v) {
49 $kid = isset($v['kid']) ? $v['kid'] : $k;
50 if ($key = self::parseKey($v)) {
51 $keys[(string) $kid] = $key;
52 }
53 }
54
55 if (0 === \count($keys)) {
56 throw new UnexpectedValueException('No supported algorithms found in JWK Set');
57 }
58
59 return $keys;
60 }
61
62 /**
63 * Parse a JWK key
64 *
65 * @param array<mixed> $jwk An individual JWK
66 *
67 * @return Key The key object for the JWK
68 *
69 * @throws InvalidArgumentException Provided JWK is empty
70 * @throws UnexpectedValueException Provided JWK was invalid
71 * @throws DomainException OpenSSL failure
72 *
73 * @uses createPemFromModulusAndExponent
74 */
75 public static function parseKey(array $jwk): ?Key
76 {
77 if (empty($jwk)) {
78 throw new InvalidArgumentException('JWK must not be empty');
79 }
80
81 if (!isset($jwk['kty'])) {
82 throw new UnexpectedValueException('JWK must contain a "kty" parameter');
83 }
84
85 if (!isset($jwk['alg'])) {
86 // The "alg" parameter is optional in a KTY, but is required for parsing in
87 // this library. Add it manually to your JWK array if it doesn't already exist.
88 // @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
89 throw new UnexpectedValueException('JWK must contain an "alg" parameter');
90 }
91
92 switch ($jwk['kty']) {
93 case 'RSA':
94 if (!empty($jwk['d'])) {
95 throw new UnexpectedValueException('RSA private keys are not supported');
96 }
97 if (!isset($jwk['n']) || !isset($jwk['e'])) {
98 throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
99 }
100
101 $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
102 $publicKey = \openssl_pkey_get_public($pem);
103 if (false === $publicKey) {
104 throw new DomainException(
105 'OpenSSL error: ' . \openssl_error_string()
106 );
107 }
108 return new Key($publicKey, $jwk['alg']);
109 default:
110 // Currently only RSA is supported
111 break;
112 }
113
114 return null;
115 }
116
117 /**
118 * Create a public key represented in PEM format from RSA modulus and exponent information
119 *
120 * @param string $n The RSA modulus encoded in Base64
121 * @param string $e The RSA exponent encoded in Base64
122 *
123 * @return string The RSA public key represented in PEM format
124 *
125 * @uses encodeLength
126 */
127 private static function createPemFromModulusAndExponent(
128 string $n,
129 string $e
130 ): string {
131 $mod = JWT::urlsafeB64Decode($n);
132 $exp = JWT::urlsafeB64Decode($e);
133
134 $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
135 $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
136
137 $rsaPublicKey = \pack(
138 'Ca*a*a*',
139 48,
140 self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
141 $modulus,
142 $publicExponent
143 );
144
145 // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
146 $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
147 $rsaPublicKey = \chr(0) . $rsaPublicKey;
148 $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
149
150 $rsaPublicKey = \pack(
151 'Ca*a*',
152 48,
153 self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
154 $rsaOID . $rsaPublicKey
155 );
156
157 $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
158 \chunk_split(\base64_encode($rsaPublicKey), 64) .
159 '-----END PUBLIC KEY-----';
160
161 return $rsaPublicKey;
162 }
163
164 /**
165 * DER-encode the length
166 *
167 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
168 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
169 *
170 * @param int $length
171 * @return string
172 */
173 private static function encodeLength(int $length): string
174 {
175 if ($length <= 0x7F) {
176 return \chr($length);
177 }
178
179 $temp = \ltrim(\pack('N', $length), \chr(0));
180
181 return \pack('Ca*', 0x80 | \strlen($temp), $temp);
182 }
183 }
184