These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * The MIT License (MIT) |
||
5 | * |
||
6 | * Copyright (c) 2014-2016 Spomky-Labs |
||
7 | * |
||
8 | * This software may be modified and distributed under the terms |
||
9 | * of the MIT license. See the LICENSE file for details. |
||
10 | */ |
||
11 | |||
12 | namespace Jose; |
||
13 | |||
14 | use Assert\Assertion; |
||
15 | use Base64Url\Base64Url; |
||
16 | |||
17 | final class Encrypter implements EncrypterInterface |
||
18 | { |
||
19 | use Behaviour\HasKeyChecker; |
||
20 | use Behaviour\HasJWAManager; |
||
21 | use Behaviour\HasCompressionManager; |
||
22 | use Behaviour\CommonCipheringMethods; |
||
23 | use Behaviour\EncrypterTrait; |
||
24 | |||
25 | /** |
||
26 | * {@inheritdoc} |
||
27 | */ |
||
28 | public static function createEncrypter(array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF', 'ZLIB', 'GZ']) |
||
29 | { |
||
30 | $encrypter = new self($key_encryption_algorithms, $content_encryption_algorithms, $compression_methods); |
||
31 | |||
32 | return $encrypter; |
||
33 | } |
||
34 | |||
35 | /** |
||
36 | * Decrypter constructor. |
||
37 | * |
||
38 | * @param string[]|\Jose\Algorithm\KeyEncryptionAlgorithmInterface[] $key_encryption_algorithms |
||
39 | * @param string[]|\Jose\Algorithm\ContentEncryptionAlgorithmInterface[] $content_encryption_algorithms |
||
40 | * @param string[]|\Jose\Compression\CompressionInterface[] $compression_methods |
||
41 | */ |
||
42 | public function __construct(array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods) |
||
43 | { |
||
44 | $this->setKeyEncryptionAlgorithms($key_encryption_algorithms); |
||
45 | $this->setContentEncryptionAlgorithms($content_encryption_algorithms); |
||
46 | $this->setCompressionMethods($compression_methods); |
||
47 | $this->setJWAManager(Factory\AlgorithmManagerFactory::createAlgorithmManager(array_merge($key_encryption_algorithms, $content_encryption_algorithms))); |
||
48 | $this->setCompressionManager(Factory\CompressionManagerFactory::createCompressionManager($compression_methods)); |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * {@inheritdoc} |
||
53 | */ |
||
54 | public function encrypt(Object\JWEInterface &$jwe) |
||
55 | { |
||
56 | Assertion::false($jwe->isEncrypted(), 'The JWE is already encrypted.'); |
||
57 | Assertion::greaterThan($jwe->countRecipients(), 0, 'The JWE does not contain recipient.'); |
||
58 | $additional_headers = []; |
||
59 | $nb_recipients = $jwe->countRecipients(); |
||
60 | $this->prepareEncryptionProcess($jwe, $content_encryption_algorithm, $compression_method, $key_management_mode, $cek, $additional_headers); |
||
61 | |||
62 | for ($i = 0; $i < $nb_recipients; $i++) { |
||
63 | $this->processRecipient($jwe, $jwe->getRecipient($i), $cek, $content_encryption_algorithm, $additional_headers); |
||
64 | } |
||
65 | |||
66 | if (!empty($additional_headers) && 1 === $jwe->countRecipients()) { |
||
67 | $jwe = $jwe->withSharedProtectedHeaders(array_merge($jwe->getSharedProtectedHeaders(), $additional_headers)); |
||
68 | } |
||
69 | |||
70 | $iv_size = $content_encryption_algorithm->getIVSize(); |
||
0 ignored issues
–
show
|
|||
71 | $iv = $this->createIV($iv_size); |
||
72 | |||
73 | $this->encryptJWE($jwe, $content_encryption_algorithm, $cek, $iv, $compression_method); |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * @param \Jose\Object\JWEInterface $jwe |
||
78 | * @param \Jose\Object\RecipientInterface $recipient |
||
79 | * @param string $cek |
||
80 | * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
81 | * @param array $additional_headers |
||
82 | */ |
||
83 | private function processRecipient(Object\JWEInterface $jwe, Object\RecipientInterface &$recipient, $cek, Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers) |
||
84 | { |
||
85 | if (null === $recipient->getRecipientKey()) { |
||
86 | return; |
||
87 | } |
||
88 | $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $recipient->getHeaders()); |
||
89 | $key_encryption_algorithm = $this->findKeyEncryptionAlgorithm($complete_headers); |
||
90 | $this->checkKeys($key_encryption_algorithm, $content_encryption_algorithm, $recipient->getRecipientKey()); |
||
91 | $encrypted_content_encryption_key = $this->getEncryptedKey($complete_headers, $cek, $key_encryption_algorithm, $content_encryption_algorithm, $additional_headers, $recipient->getRecipientKey()); |
||
92 | $recipient_headers = $recipient->getHeaders(); |
||
93 | if (!empty($additional_headers) && 1 !== $jwe->countRecipients()) { |
||
94 | $recipient_headers = array_merge($recipient_headers, $additional_headers); |
||
95 | $additional_headers = []; |
||
96 | } |
||
97 | |||
98 | $recipient = Object\Recipient::createRecipientFromLoadedJWE($recipient_headers, $encrypted_content_encryption_key); |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * @param \Jose\Object\JWEInterface $jwe |
||
103 | * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
104 | * @param string $cek |
||
105 | * @param string $iv |
||
106 | * @param \Jose\Compression\CompressionInterface|null $compression_method |
||
107 | */ |
||
108 | private function encryptJWE(Object\JWEInterface &$jwe, Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm, $cek, $iv, Compression\CompressionInterface $compression_method = null) |
||
109 | { |
||
110 | if (!empty($jwe->getSharedProtectedHeaders())) { |
||
111 | $jwe = $jwe->withEncodedSharedProtectedHeaders(Base64Url::encode(json_encode($jwe->getSharedProtectedHeaders()))); |
||
112 | } |
||
113 | |||
114 | $tag = null; |
||
115 | $payload = $this->preparePayload($jwe->getPayload(), $compression_method); |
||
116 | $aad = null === $jwe->getAAD() ? null : Base64Url::encode($jwe->getAAD()); |
||
117 | $ciphertext = $content_encryption_algorithm->encryptContent($payload, $cek, $iv, $aad, $jwe->getEncodedSharedProtectedHeaders(), $tag); |
||
118 | $jwe = $jwe->withCiphertext($ciphertext); |
||
119 | $jwe = $jwe->withIV($iv); |
||
120 | |||
121 | if (null !== $tag) { |
||
122 | $jwe = $jwe->withTag($tag); |
||
123 | } |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface $key_encryption_algorithm |
||
128 | * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
129 | * @param \Jose\Object\JWKInterface $recipient_key |
||
130 | */ |
||
131 | private function checkKeys(Algorithm\KeyEncryptionAlgorithmInterface $key_encryption_algorithm, Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm, Object\JWKInterface $recipient_key) |
||
132 | { |
||
133 | $this->checkKeyUsage($recipient_key, 'encryption'); |
||
134 | if ('dir' !== $key_encryption_algorithm->getAlgorithmName()) { |
||
135 | $this->checkKeyAlgorithm($recipient_key, $key_encryption_algorithm->getAlgorithmName()); |
||
136 | } else { |
||
137 | $this->checkKeyAlgorithm($recipient_key, $content_encryption_algorithm->getAlgorithmName()); |
||
138 | } |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * @param string $payload |
||
143 | * @param \Jose\Compression\CompressionInterface|null $compression_method |
||
144 | * |
||
145 | * @return string |
||
146 | */ |
||
147 | private function preparePayload($payload, Compression\CompressionInterface $compression_method = null) |
||
148 | { |
||
149 | $prepared = is_string($payload) ? $payload : json_encode($payload); |
||
150 | Assertion::notNull($prepared, 'The payload is empty or cannot encoded into JSON.'); |
||
151 | |||
152 | if (null === $compression_method) { |
||
153 | return $prepared; |
||
154 | } |
||
155 | $compressed_payload = $compression_method->compress($prepared); |
||
156 | Assertion::string($compressed_payload, 'Compression failed.'); |
||
157 | |||
158 | return $compressed_payload; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * @param array $complete_headers |
||
163 | * @param string $cek |
||
164 | * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface $key_encryption_algorithm |
||
165 | * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
166 | * @param \Jose\Object\JWKInterface $recipient_key |
||
167 | * @param array $additional_headers |
||
168 | * |
||
169 | * @return string|null |
||
170 | */ |
||
171 | private function getEncryptedKey(array $complete_headers, $cek, Algorithm\KeyEncryptionAlgorithmInterface $key_encryption_algorithm, Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, Object\JWKInterface $recipient_key) |
||
172 | { |
||
173 | if ($key_encryption_algorithm instanceof Algorithm\KeyEncryption\KeyEncryptionInterface) { |
||
174 | return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers); |
||
175 | } elseif ($key_encryption_algorithm instanceof Algorithm\KeyEncryption\KeyWrappingInterface) { |
||
176 | return $this->getEncryptedKeyFromKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers); |
||
177 | } elseif ($key_encryption_algorithm instanceof Algorithm\KeyEncryption\KeyAgreementWrappingInterface) { |
||
178 | return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $content_encryption_algorithm, $additional_headers, $recipient_key); |
||
179 | } |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * @param array $complete_headers |
||
184 | * @param string $cek |
||
185 | * @param \Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface $key_encryption_algorithm |
||
186 | * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
187 | * @param array $additional_headers |
||
188 | * @param \Jose\Object\JWKInterface $recipient_key |
||
189 | * |
||
190 | * @return string |
||
191 | */ |
||
192 | private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $complete_headers, $cek, Algorithm\KeyEncryption\KeyAgreementWrappingInterface $key_encryption_algorithm, Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, Object\JWKInterface $recipient_key) |
||
193 | { |
||
194 | $jwt_cek = $key_encryption_algorithm->wrapAgreementKey($recipient_key, $cek, $content_encryption_algorithm->getCEKSize(), $complete_headers, $additional_headers); |
||
195 | |||
196 | return $jwt_cek; |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * @param array $complete_headers |
||
201 | * @param string $cek |
||
202 | * @param \Jose\Algorithm\KeyEncryption\KeyEncryptionInterface $key_encryption_algorithm |
||
203 | * @param \Jose\Object\JWKInterface $recipient_key |
||
204 | * @param array $additional_headers |
||
205 | * |
||
206 | * @return string |
||
207 | */ |
||
208 | private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $complete_headers, $cek, Algorithm\KeyEncryption\KeyEncryptionInterface $key_encryption_algorithm, Object\JWKInterface $recipient_key, array &$additional_headers) |
||
209 | { |
||
210 | return $key_encryption_algorithm->encryptKey($recipient_key, $cek, $complete_headers, $additional_headers); |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * @param array $complete_headers |
||
215 | * @param string $cek |
||
216 | * @param \Jose\Algorithm\KeyEncryption\KeyWrappingInterface $key_encryption_algorithm |
||
217 | * @param \Jose\Object\JWKInterface $recipient_key |
||
218 | * @param array $additional_headers |
||
219 | * |
||
220 | * @return string |
||
221 | */ |
||
222 | private function getEncryptedKeyFromKeyWrappingAlgorithm(array $complete_headers, $cek, Algorithm\KeyEncryption\KeyWrappingInterface $key_encryption_algorithm, Object\JWKInterface $recipient_key, &$additional_headers) |
||
223 | { |
||
224 | return $key_encryption_algorithm->wrapKey($recipient_key, $cek, $complete_headers, $additional_headers); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * @param array $complete_headers |
||
229 | * |
||
230 | * @return \Jose\Algorithm\KeyEncryptionAlgorithmInterface |
||
231 | */ |
||
232 | private function findKeyEncryptionAlgorithm(array $complete_headers) |
||
233 | { |
||
234 | Assertion::keyExists($complete_headers, 'alg', 'Parameter "alg" is missing.'); |
||
235 | $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']); |
||
236 | Assertion::isInstanceOf($key_encryption_algorithm, Algorithm\KeyEncryptionAlgorithmInterface::class, sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $complete_headers['alg'])); |
||
237 | |||
238 | return $key_encryption_algorithm; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * @param int $size |
||
243 | * |
||
244 | * @return string |
||
245 | */ |
||
246 | private function createIV($size) |
||
247 | { |
||
248 | return random_bytes($size / 8); |
||
249 | } |
||
250 | } |
||
251 |
Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.