PluginProbe
Media Cloud Sync / trunk
Media Cloud Sync vtrunk
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / google / firebase / php-jwt / src / JWK.php

JWK.php in Media Cloud Sync trunk, at includes/sdk/google/firebase/php-jwt/src/JWK.php

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