PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / web-token / jwt-core / JWKSet.php

JWKSet.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at vendor/web-token/jwt-core/JWKSet.php

341 lines 8.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 /*
6 * The MIT License (MIT)
7 *
8 * Copyright (c) 2014-2020 Spomky-Labs
9 *
10 * This software may be modified and distributed under the terms
11 * of the MIT license. See the LICENSE file for details.
12 */
13
14 namespace Jose\Component\Core;
15
16 use function array_key_exists;
17 use ArrayIterator;
18 use function count;
19 use Countable;
20 use function in_array;
21 use InvalidArgumentException;
22 use function is_array;
23 use IteratorAggregate;
24 use JsonSerializable;
25 use Traversable;
26
27 class JWKSet implements Countable, IteratorAggregate, JsonSerializable
28 {
29 /**
30 * @var array
31 */
32 private $keys = [];
33
34 /**
35 * @param JWK[] $keys
36 *
37 * @throws InvalidArgumentException if the list is invalid
38 */
39 public function __construct(array $keys)
40 {
41 foreach ($keys as $k => $key) {
42 if (!$key instanceof JWK) {
43 throw new InvalidArgumentException('Invalid list. Should only contains JWK objects');
44 }
45
46 if ($key->has('kid')) {
47 unset($keys[$k]);
48 $this->keys[$key->get('kid')] = $key;
49 } else {
50 $this->keys[] = $key;
51 }
52 }
53 }
54
55 /**
56 * Creates a JWKSet object using the given values.
57 *
58 * @throws InvalidArgumentException if the keyset is not valid
59 *
60 * @return JWKSet
61 */
62 public static function createFromKeyData(array $data): self
63 {
64 if (!isset($data['keys'])) {
65 throw new InvalidArgumentException('Invalid data.');
66 }
67 if (!is_array($data['keys'])) {
68 throw new InvalidArgumentException('Invalid data.');
69 }
70
71 $jwkset = new self([]);
72 foreach ($data['keys'] as $key) {
73 $jwk = new JWK($key);
74 if ($jwk->has('kid')) {
75 $jwkset->keys[$jwk->get('kid')] = $jwk;
76 } else {
77 $jwkset->keys[] = $jwk;
78 }
79 }
80
81 return $jwkset;
82 }
83
84 /**
85 * Creates a JWKSet object using the given Json string.
86 *
87 * @throws InvalidArgumentException if the data is not valid
88 *
89 * @return JWKSet
90 */
91 public static function createFromJson(string $json): self
92 {
93 $data = json_decode($json, true);
94 if (!is_array($data)) {
95 throw new InvalidArgumentException('Invalid argument.');
96 }
97
98 return self::createFromKeyData($data);
99 }
100
101 /**
102 * Returns an array of keys stored in the key set.
103 *
104 * @return JWK[]
105 */
106 public function all(): array
107 {
108 return $this->keys;
109 }
110
111 /**
112 * Add key to store in the key set.
113 * This method is immutable and will return a new object.
114 *
115 * @return JWKSet
116 */
117 public function with(JWK $jwk): self
118 {
119 $clone = clone $this;
120
121 if ($jwk->has('kid')) {
122 $clone->keys[$jwk->get('kid')] = $jwk;
123 } else {
124 $clone->keys[] = $jwk;
125 }
126
127 return $clone;
128 }
129
130 /**
131 * Remove key from the key set.
132 * This method is immutable and will return a new object.
133 *
134 * @param int|string $key Key to remove from the key set
135 *
136 * @return JWKSet
137 */
138 public function without($key): self
139 {
140 if (!$this->has($key)) {
141 return $this;
142 }
143
144 $clone = clone $this;
145 unset($clone->keys[$key]);
146
147 return $clone;
148 }
149
150 /**
151 * Returns true if the key set contains a key with the given index.
152 *
153 * @param int|string $index
154 */
155 public function has($index): bool
156 {
157 return array_key_exists($index, $this->keys);
158 }
159
160 /**
161 * Returns the key with the given index. Throws an exception if the index is not present in the key store.
162 *
163 * @param int|string $index
164 *
165 * @throws InvalidArgumentException if the index is not defined
166 */
167 public function get($index): JWK
168 {
169 if (!$this->has($index)) {
170 throw new InvalidArgumentException('Undefined index.');
171 }
172
173 return $this->keys[$index];
174 }
175
176 /**
177 * Returns the values to be serialized.
178 */
179 public function jsonSerialize(): array
180 {
181 return ['keys' => array_values($this->keys)];
182 }
183
184 /**
185 * Returns the number of keys in the key set.
186 *
187 * @param int $mode
188 */
189 public function count($mode = COUNT_NORMAL): int
190 {
191 return count($this->keys, $mode);
192 }
193
194 /**
195 * Try to find a key that fits on the selected requirements.
196 * Returns null if not found.
197 *
198 * @param string $type Must be 'sig' (signature) or 'enc' (encryption)
199 * @param null|Algorithm $algorithm Specifies the algorithm to be used
200 * @param array $restrictions More restrictions such as 'kid' or 'kty'
201 *
202 * @throws InvalidArgumentException if the key type is not valid (must be "sig" or "enc")
203 */
204 public function selectKey(string $type, ?Algorithm $algorithm = null, array $restrictions = []): ?JWK
205 {
206 if (!in_array($type, ['enc', 'sig'], true)) {
207 throw new InvalidArgumentException('Allowed key types are "sig" or "enc".');
208 }
209
210 $result = [];
211 foreach ($this->keys as $key) {
212 $ind = 0;
213
214 $can_use = $this->canKeyBeUsedFor($type, $key);
215 if (false === $can_use) {
216 continue;
217 }
218 $ind += $can_use;
219
220 $alg = $this->canKeyBeUsedWithAlgorithm($algorithm, $key);
221 if (false === $alg) {
222 continue;
223 }
224 $ind += $alg;
225
226 if (false === $this->doesKeySatisfyRestrictions($restrictions, $key)) {
227 continue;
228 }
229
230 $result[] = ['key' => $key, 'ind' => $ind];
231 }
232
233 if (0 === count($result)) {
234 return null;
235 }
236
237 usort($result, [$this, 'sortKeys']);
238
239 return $result[0]['key'];
240 }
241
242 /**
243 * Internal method only. Should not be used.
244 *
245 * @internal
246 * @internal
247 */
248 public static function sortKeys(array $a, array $b): int
249 {
250 if ($a['ind'] === $b['ind']) {
251 return 0;
252 }
253
254 return ($a['ind'] > $b['ind']) ? -1 : 1;
255 }
256
257 /**
258 * Internal method only. Should not be used.
259 *
260 * @internal
261 */
262 public function getIterator(): Traversable
263 {
264 return new ArrayIterator($this->keys);
265 }
266
267 /**
268 * @throws InvalidArgumentException if the key does not fulfill with the "key_ops" constraint
269 *
270 * @return bool|int
271 */
272 private function canKeyBeUsedFor(string $type, JWK $key)
273 {
274 if ($key->has('use')) {
275 return $type === $key->get('use') ? 1 : false;
276 }
277 if ($key->has('key_ops')) {
278 $key_ops = $key->get('key_ops');
279 if (!is_array($key_ops)) {
280 throw new InvalidArgumentException('Invalid key parameter "key_ops". Should be a list of key operations');
281 }
282
283 return $type === self::convertKeyOpsToKeyUse($key_ops) ? 1 : false;
284 }
285
286 return 0;
287 }
288
289 /**
290 * @return bool|int
291 */
292 private function canKeyBeUsedWithAlgorithm(?Algorithm $algorithm, JWK $key)
293 {
294 if (null === $algorithm) {
295 return 0;
296 }
297 if (!in_array($key->get('kty'), $algorithm->allowedKeyTypes(), true)) {
298 return false;
299 }
300 if ($key->has('alg')) {
301 return $algorithm->name() === $key->get('alg') ? 2 : false;
302 }
303
304 return 1;
305 }
306
307 private function doesKeySatisfyRestrictions(array $restrictions, JWK $key): bool
308 {
309 foreach ($restrictions as $k => $v) {
310 if (!$key->has($k) || $v !== $key->get($k)) {
311 return false;
312 }
313 }
314
315 return true;
316 }
317
318 /**
319 * @throws InvalidArgumentException if the key operation is not supported
320 */
321 private static function convertKeyOpsToKeyUse(array $key_ops): string
322 {
323 switch (true) {
324 case in_array('verify', $key_ops, true):
325 case in_array('sign', $key_ops, true):
326 return 'sig';
327
328 case in_array('encrypt', $key_ops, true):
329 case in_array('decrypt', $key_ops, true):
330 case in_array('wrapKey', $key_ops, true):
331 case in_array('unwrapKey', $key_ops, true):
332 case in_array('deriveKey', $key_ops, true):
333 case in_array('deriveBits', $key_ops, true):
334 return 'enc';
335
336 default:
337 throw new InvalidArgumentException(sprintf('Unsupported key operation value "%s"', implode(', ', $key_ops)));
338 }
339 }
340 }
341