PluginProbe ʕ •ᴥ•ʔ
Site Kit by Google – Analytics, Search Console, AdSense, Speed / 1.177.0
Site Kit by Google – Analytics, Search Console, AdSense, Speed v1.177.0
1.180.0 1.179.0 1.178.0 1.177.0 1.0.4 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.10.0 1.101.0 1.102.0 1.103.0 1.104.0 1.105.0 1.106.0 1.107.0 1.108.0 1.109.0 1.11.0 1.11.1 1.110.0 1.111.0 1.111.1 1.113.0 1.114.0 1.115.0 1.116.0 1.118.0 1.119.0 1.12.0 1.120.0 1.121.0 1.122.0 1.123.0 1.123.1 1.124.0 1.125.0 1.126.0 1.127.0 1.128.0 1.128.1 1.129.0 1.129.1 1.13.0 1.13.1 1.130.0 1.131.0 1.132.0 1.133.0 1.134.0 1.135.0 1.136.0 1.137.0 1.138.0 1.139.0 1.14.0 1.140.0 1.141.0 1.142.0 1.144.0 1.145.0 1.146.0 1.147.0 1.148.0 1.149.0 1.149.1 1.15.0 1.150.0 1.151.0 1.152.0 1.152.1 1.153.0 1.154.0 1.155.0 1.156.0 1.157.0 1.158.0 1.159.0 1.16.0 1.160.0 1.160.1 1.161.0 1.162.0 1.162.1 1.163.0 1.164.0 1.165.0 1.166.0 1.167.0 1.168.0 1.17.0 1.170.0 1.171.0 1.172.0 1.173.0 1.174.0 1.175.0 1.176.0 1.18.0 1.19.0 1.2.0 1.20.0 1.21.0 1.22.0 1.23.0 1.24.0 1.25.0 1.26.0 1.27.0 1.28.0 1.29.0 1.3.0 1.3.1 1.30.0 1.31.0 1.32.0 1.33.0 1.34.0 1.34.1 1.35.0 1.36.0 1.37.0 1.38.0 1.38.1 1.39.0 1.4.0 1.40.0 1.41.0 1.42.0 1.43.0 1.44.0 1.45.0 1.46.0 1.47.0 1.48.0 1.48.1 1.49.0 1.49.1 1.5.0 1.5.1 1.50.0 1.6.0 1.68.0 1.69.0 1.7.0 1.7.1 1.70.0 1.71.0 1.72.0 1.73.0 1.74.0 1.75.0 1.77.0 1.78.0 1.79.0 1.79.1 1.8.0 1.8.1 1.80.0 1.81.0 1.82.0 1.83.0 1.84.0 1.85.0 1.86.0 1.87.0 1.88.0 1.89.0 1.9.0 1.90.0 1.90.1 1.92.0 1.93.0 1.94.0 1.95.0 1.96.0 1.98.0 1.99.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3
google-site-kit / third-party / firebase / php-jwt / src / JWK.php
google-site-kit / third-party / firebase / php-jwt / src Last commit date
BeforeValidException.php 1 month ago CachedKeySet.php 1 month ago ExpiredException.php 1 month ago JWK.php 1 month ago JWT.php 1 month ago JWTExceptionWithPayloadInterface.php 1 month ago Key.php 1 month ago SignatureInvalidException.php 1 year ago
JWK.php
268 lines
1 <?php
2
3 namespace Google\Site_Kit_Dependencies\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(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(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 default:
153 break;
154 }
155 return null;
156 }
157 /**
158 * Converts the EC JWK values to pem format.
159 *
160 * @param string $crv The EC curve (only P-256 & P-384 is supported)
161 * @param string $x The EC x-coordinate
162 * @param string $y The EC y-coordinate
163 *
164 * @return string
165 */
166 private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
167 {
168 $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)));
169 return sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", wordwrap(base64_encode($pem), 64, "\n", \true));
170 }
171 /**
172 * Create a public key represented in PEM format from RSA modulus and exponent information
173 *
174 * @param string $n The RSA modulus encoded in Base64
175 * @param string $e The RSA exponent encoded in Base64
176 *
177 * @return string The RSA public key represented in PEM format
178 *
179 * @uses encodeLength
180 */
181 private static function createPemFromModulusAndExponent(string $n, string $e): string
182 {
183 $mod = JWT::urlsafeB64Decode($n);
184 $exp = JWT::urlsafeB64Decode($e);
185 $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
186 $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
187 $rsaPublicKey = \pack('Ca*a*a*', 48, self::encodeLength(\strlen($modulus) + \strlen($publicExponent)), $modulus, $publicExponent);
188 // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
189 $rsaOID = \pack('H*', '300d06092a864886f70d0101010500');
190 // hex version of MA0GCSqGSIb3DQEBAQUA
191 $rsaPublicKey = \chr(0) . $rsaPublicKey;
192 $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
193 $rsaPublicKey = \pack('Ca*a*', 48, self::encodeLength(\strlen($rsaOID . $rsaPublicKey)), $rsaOID . $rsaPublicKey);
194 return "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($rsaPublicKey), 64) . '-----END PUBLIC KEY-----';
195 }
196 /**
197 * DER-encode the length
198 *
199 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
200 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
201 *
202 * @param int $length
203 * @return string
204 */
205 private static function encodeLength(int $length): string
206 {
207 if ($length <= 0x7f) {
208 return \chr($length);
209 }
210 $temp = \ltrim(\pack('N', $length), \chr(0));
211 return \pack('Ca*', 0x80 | \strlen($temp), $temp);
212 }
213 /**
214 * Encodes a value into a DER object.
215 * Also defined in Firebase\JWT\JWT
216 *
217 * @param int $type DER tag
218 * @param string $value the value to encode
219 * @return string the encoded object
220 */
221 private static function encodeDER(int $type, string $value): string
222 {
223 $tag_header = 0;
224 if ($type === self::ASN1_SEQUENCE) {
225 $tag_header |= 0x20;
226 }
227 // Type
228 $der = \chr($tag_header | $type);
229 // Length
230 $der .= \chr(\strlen($value));
231 return $der . $value;
232 }
233 /**
234 * Encodes a string into a DER-encoded OID.
235 *
236 * @param string $oid the OID string
237 * @return string the binary DER-encoded OID
238 */
239 private static function encodeOID(string $oid): string
240 {
241 $octets = explode('.', $oid);
242 // Get the first octet
243 $first = (int) array_shift($octets);
244 $second = (int) array_shift($octets);
245 $oid = \chr($first * 40 + $second);
246 // Iterate over subsequent octets
247 foreach ($octets as $octet) {
248 if ($octet == 0) {
249 $oid .= \chr(0x0);
250 continue;
251 }
252 $bin = '';
253 while ($octet) {
254 $bin .= \chr(0x80 | $octet & 0x7f);
255 $octet >>= 7;
256 }
257 $bin[0] = $bin[0] & \chr(0x7f);
258 // Convert to big endian if necessary
259 if (pack('V', 65534) == pack('L', 65534)) {
260 $oid .= strrev($bin);
261 } else {
262 $oid .= $bin;
263 }
264 }
265 return $oid;
266 }
267 }
268