This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | /* |
||
6 | * The MIT License (MIT) |
||
7 | * |
||
8 | * Copyright (c) 2014-2017 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\Encryption; |
||
15 | |||
16 | use Jose\Component\Checker\HeaderCheckerManager; |
||
17 | use Jose\Component\Core\AlgorithmManager; |
||
18 | use Jose\Component\Core\JWKSet; |
||
19 | use Jose\Component\Encryption\Compression\CompressionMethodManager; |
||
20 | use Base64Url\Base64Url; |
||
21 | use Jose\Component\Core\AlgorithmInterface; |
||
22 | use Jose\Component\Core\JWK; |
||
23 | use Jose\Component\Core\Util\KeyChecker; |
||
24 | use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithmInterface; |
||
25 | use Jose\Component\Encryption\Algorithm\KeyEncryption\DirectEncryptionInterface; |
||
26 | use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyAgreementInterface; |
||
27 | use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyAgreementWrappingInterface; |
||
28 | use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyEncryptionInterface; |
||
29 | use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyWrappingInterface; |
||
30 | use Jose\Component\Encryption\Algorithm\KeyEncryptionAlgorithmInterface; |
||
31 | use Jose\Component\Encryption\Serializer\JWESerializerManager; |
||
32 | |||
33 | /** |
||
34 | * Class JWELoader. |
||
35 | */ |
||
36 | final class JWELoader |
||
37 | { |
||
38 | /** |
||
39 | * @var HeaderCheckerManager |
||
40 | */ |
||
41 | private $headerCheckerManager; |
||
42 | |||
43 | /** |
||
44 | * @var AlgorithmManager |
||
45 | */ |
||
46 | private $keyEncryptionAlgorithmManager; |
||
47 | |||
48 | /** |
||
49 | * @var AlgorithmManager |
||
50 | */ |
||
51 | private $contentEncryptionAlgorithmManager; |
||
52 | |||
53 | /** |
||
54 | * @var CompressionMethodManager |
||
55 | */ |
||
56 | private $compressionMethodManager; |
||
57 | |||
58 | /** |
||
59 | * @var JWESerializerManager |
||
60 | */ |
||
61 | private $serializerManager; |
||
62 | |||
63 | /** |
||
64 | * JWELoader constructor. |
||
65 | * |
||
66 | * @param AlgorithmManager $keyEncryptionAlgorithmManager |
||
67 | * @param AlgorithmManager $contentEncryptionAlgorithmManager |
||
68 | * @param CompressionMethodManager $compressionMethodManager |
||
69 | * @param HeaderCheckerManager $headerCheckerManager |
||
70 | * @param JWESerializerManager $serializerManager |
||
71 | */ |
||
72 | public function __construct(AlgorithmManager $keyEncryptionAlgorithmManager, AlgorithmManager $contentEncryptionAlgorithmManager, CompressionMethodManager $compressionMethodManager, HeaderCheckerManager $headerCheckerManager, JWESerializerManager $serializerManager) |
||
73 | { |
||
74 | $this->keyEncryptionAlgorithmManager = $keyEncryptionAlgorithmManager; |
||
75 | $this->contentEncryptionAlgorithmManager = $contentEncryptionAlgorithmManager; |
||
76 | $this->compressionMethodManager = $compressionMethodManager; |
||
77 | $this->headerCheckerManager = $headerCheckerManager; |
||
78 | $this->serializerManager = $serializerManager; |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * @param string $input |
||
83 | * @param string|null $serializer |
||
84 | * |
||
85 | * @return JWE |
||
86 | */ |
||
87 | public function load(string $input, ?string &$serializer = null): JWE |
||
88 | { |
||
89 | return $this->serializerManager->unserialize($input, $serializer); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * @return AlgorithmManager |
||
94 | */ |
||
95 | public function getKeyEncryptionAlgorithmManager(): AlgorithmManager |
||
96 | { |
||
97 | return $this->keyEncryptionAlgorithmManager; |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * @return AlgorithmManager |
||
102 | */ |
||
103 | public function getContentEncryptionAlgorithmManager(): AlgorithmManager |
||
104 | { |
||
105 | return $this->contentEncryptionAlgorithmManager; |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * @return CompressionMethodManager |
||
110 | */ |
||
111 | public function getCompressionMethodManager(): CompressionMethodManager |
||
112 | { |
||
113 | return $this->compressionMethodManager; |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * @param JWE $jwe A JWE object to decrypt |
||
118 | * @param JWK $jwk The key used to decrypt the input |
||
119 | * @param null|int $recipientIndex If the JWE has been decrypted, an integer that represents the ID of the recipient is set |
||
120 | * |
||
121 | * @return JWE |
||
122 | */ |
||
123 | public function decryptUsingKey(JWE $jwe, JWK $jwk, ?int &$recipientIndex = null): JWE |
||
124 | { |
||
125 | $jwkset = JWKSet::createFromKeys([$jwk]); |
||
126 | |||
127 | $jwe = $this->decryptUsingKeySet($jwe, $jwkset, $recipientIndex); |
||
128 | $this->headerCheckerManager->check($jwe, $recipientIndex); |
||
129 | |||
130 | return $jwe; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * @param JWE $jwe A JWE object to decrypt |
||
135 | * @param JWKSet $jwkset The key set used to decrypt the input |
||
136 | * @param null|int $recipientIndex If the JWE has been decrypted, an integer that represents the ID of the recipient is set |
||
137 | * |
||
138 | * @return JWE |
||
139 | */ |
||
140 | public function decryptUsingKeySet(JWE $jwe, JWKSet $jwkset, ?int &$recipientIndex = null): JWE |
||
141 | { |
||
142 | $this->checkJWKSet($jwkset); |
||
143 | $this->checkPayload($jwe); |
||
144 | $this->checkRecipients($jwe); |
||
145 | |||
146 | $nb_recipients = $jwe->countRecipients(); |
||
147 | |||
148 | for ($i = 0; $i < $nb_recipients; ++$i) { |
||
149 | $plaintext = $this->decryptRecipientKey($jwe, $jwkset, $i); |
||
150 | if (null !== $plaintext) { |
||
151 | $recipientIndex = $i; |
||
152 | |||
153 | return $jwe->withPayload($plaintext); |
||
154 | } |
||
155 | } |
||
156 | |||
157 | throw new \InvalidArgumentException('Unable to decrypt the JWE.'); |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * @param JWE $jwe |
||
162 | * @param JWKSet $jwkset |
||
163 | * @param int $i |
||
164 | * |
||
165 | * @return string|null |
||
166 | */ |
||
167 | private function decryptRecipientKey(JWE $jwe, JWKSet $jwkset, int $i): ?string |
||
168 | { |
||
169 | $recipient = $jwe->getRecipient($i); |
||
170 | $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $recipient->getHeaders()); |
||
171 | $this->checkCompleteHeader($complete_headers); |
||
172 | |||
173 | $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($complete_headers); |
||
174 | $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($complete_headers); |
||
175 | |||
176 | foreach ($jwkset as $jwk) { |
||
177 | try { |
||
178 | KeyChecker::checkKeyUsage($jwk, 'decryption'); |
||
0 ignored issues
–
show
|
|||
179 | if ('dir' !== $key_encryption_algorithm->name()) { |
||
180 | KeyChecker::checkKeyAlgorithm($jwk, $key_encryption_algorithm->name()); |
||
0 ignored issues
–
show
It seems like
$jwk defined by $jwk on line 176 can be null ; however, Jose\Component\Core\Util...er::checkKeyAlgorithm() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
181 | } else { |
||
182 | KeyChecker::checkKeyAlgorithm($jwk, $content_encryption_algorithm->name()); |
||
0 ignored issues
–
show
It seems like
$jwk defined by $jwk on line 176 can be null ; however, Jose\Component\Core\Util...er::checkKeyAlgorithm() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
183 | } |
||
184 | $cek = $this->decryptCEK($key_encryption_algorithm, $content_encryption_algorithm, $jwk, $recipient, $complete_headers); |
||
0 ignored issues
–
show
It seems like
$jwk defined by $jwk on line 176 can be null ; however, Jose\Component\Encryption\JWELoader::decryptCEK() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
185 | if (null !== $cek) { |
||
186 | return $this->decryptPayload($jwe, $cek, $content_encryption_algorithm, $complete_headers); |
||
187 | } |
||
188 | } catch (\Exception $e) { |
||
189 | //We do nothing, we continue with other keys |
||
190 | continue; |
||
191 | } |
||
192 | } |
||
193 | |||
194 | return null; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * @param JWE $jwe |
||
199 | */ |
||
200 | private function checkRecipients(JWE $jwe) |
||
201 | { |
||
202 | if (0 === $jwe->countRecipients()) { |
||
203 | throw new \InvalidArgumentException('The JWE does not contain any recipient.'); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * @param JWE $jwe |
||
209 | */ |
||
210 | private function checkPayload(JWE $jwe) |
||
211 | { |
||
212 | if (null !== $jwe->getPayload()) { |
||
213 | throw new \InvalidArgumentException('The JWE is already decrypted.'); |
||
214 | } |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * @param JWKSet $jwkset |
||
219 | */ |
||
220 | private function checkJWKSet(JWKSet $jwkset) |
||
221 | { |
||
222 | if (0 === $jwkset->count()) { |
||
223 | throw new \InvalidArgumentException('No key in the key set.'); |
||
224 | } |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * @param AlgorithmInterface $key_encryption_algorithm |
||
229 | * @param ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
230 | * @param JWK $key |
||
231 | * @param Recipient $recipient |
||
232 | * @param array $complete_headers |
||
233 | * |
||
234 | * @return null|string |
||
235 | */ |
||
236 | private function decryptCEK(AlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, JWK $key, Recipient $recipient, array $complete_headers): ?string |
||
237 | { |
||
238 | if ($key_encryption_algorithm instanceof DirectEncryptionInterface) { |
||
239 | return $key_encryption_algorithm->getCEK($key); |
||
240 | } elseif ($key_encryption_algorithm instanceof KeyAgreementInterface) { |
||
241 | return $key_encryption_algorithm->getAgreementKey($content_encryption_algorithm->getCEKSize(), $content_encryption_algorithm->name(), $key, $complete_headers); |
||
242 | } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) { |
||
243 | return $key_encryption_algorithm->unwrapAgreementKey($key, $recipient->getEncryptedKey(), $content_encryption_algorithm->getCEKSize(), $complete_headers); |
||
244 | } elseif ($key_encryption_algorithm instanceof KeyEncryptionInterface) { |
||
245 | return $key_encryption_algorithm->decryptKey($key, $recipient->getEncryptedKey(), $complete_headers); |
||
246 | } elseif ($key_encryption_algorithm instanceof KeyWrappingInterface) { |
||
247 | return $key_encryption_algorithm->unwrapKey($key, $recipient->getEncryptedKey(), $complete_headers); |
||
248 | } else { |
||
249 | throw new \InvalidArgumentException('Unsupported CEK generation'); |
||
250 | } |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * @param JWE $jwe |
||
255 | * @param string $cek |
||
256 | * @param ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
257 | * @param array $complete_headers |
||
258 | * |
||
259 | * @return string |
||
260 | */ |
||
261 | private function decryptPayload(JWE $jwe, string $cek, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array $complete_headers): string |
||
262 | { |
||
263 | $payload = $content_encryption_algorithm->decryptContent($jwe->getCiphertext(), $cek, $jwe->getIV(), null === $jwe->getAAD() ? null : Base64Url::encode($jwe->getAAD()), $jwe->getEncodedSharedProtectedHeaders(), $jwe->getTag()); |
||
264 | if (null === $payload) { |
||
265 | throw new \RuntimeException('Unable to decrypt the JWE.'); |
||
266 | } |
||
267 | |||
268 | return $this->decompressIfNeeded($payload, $complete_headers); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * @param string $payload |
||
273 | * @param array $complete_headers |
||
274 | * |
||
275 | * @return string |
||
276 | */ |
||
277 | private function decompressIfNeeded(string $payload, array $complete_headers): string |
||
278 | { |
||
279 | if (array_key_exists('zip', $complete_headers)) { |
||
280 | $compression_method = $this->compressionMethodManager->get($complete_headers['zip']); |
||
281 | $payload = $compression_method->uncompress($payload); |
||
282 | if (!is_string($payload)) { |
||
283 | throw new \InvalidArgumentException('Decompression failed'); |
||
284 | } |
||
285 | } |
||
286 | |||
287 | return $payload; |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * @param array $complete_headers |
||
292 | * |
||
293 | * @throws \InvalidArgumentException |
||
294 | */ |
||
295 | private function checkCompleteHeader(array $complete_headers) |
||
296 | { |
||
297 | foreach (['enc', 'alg'] as $key) { |
||
298 | if (!array_key_exists($key, $complete_headers)) { |
||
299 | throw new \InvalidArgumentException(sprintf("Parameters '%s' is missing.", $key)); |
||
300 | } |
||
301 | } |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * @param array $complete_headers |
||
306 | * |
||
307 | * @return KeyEncryptionAlgorithmInterface |
||
308 | */ |
||
309 | private function getKeyEncryptionAlgorithm(array $complete_headers): KeyEncryptionAlgorithmInterface |
||
310 | { |
||
311 | $key_encryption_algorithm = $this->keyEncryptionAlgorithmManager->get($complete_headers['alg']); |
||
312 | if (!$key_encryption_algorithm instanceof KeyEncryptionAlgorithmInterface) { |
||
313 | throw new \InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or does not implement KeyEncryptionAlgorithmInterface.', $complete_headers['alg'])); |
||
314 | } |
||
315 | |||
316 | return $key_encryption_algorithm; |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * @param array $complete_headers |
||
321 | * |
||
322 | * @return ContentEncryptionAlgorithmInterface |
||
323 | */ |
||
324 | private function getContentEncryptionAlgorithm(array $complete_headers): ContentEncryptionAlgorithmInterface |
||
325 | { |
||
326 | $content_encryption_algorithm = $this->contentEncryptionAlgorithmManager->get($complete_headers['enc']); |
||
327 | if (!$content_encryption_algorithm instanceof ContentEncryptionAlgorithmInterface) { |
||
328 | throw new \InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or does not implement ContentEncryptionInterface.', $complete_headers['enc'])); |
||
329 | } |
||
330 | |||
331 | return $content_encryption_algorithm; |
||
332 | } |
||
333 | } |
||
334 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: