[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |