PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.15
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.15
1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
woocommerce-pos / vendor_prefixed / firebase / php-jwt / src / JWK.php

JWK.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.15, at vendor_prefixed/firebase/php-jwt/src/JWK.php

268 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WCPOS\Vendor\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