Failed Conditions
Push — v7 ( 5d1eb6...6055df )
by Florent
02:59
created

JWEBuilder   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 492
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
wmc 56
lcom 1
cbo 13
dl 0
loc 492
rs 6.5957
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getSupportedKeyEncryptionAlgorithms() 0 4 1
A getSupportedContentEncryptionAlgorithms() 0 4 1
A getSupportedCompressionMethods() 0 4 1
A withPayload() 0 7 1
A withAAD() 0 7 1
A withSharedProtectedHeaders() 0 7 1
A withSharedHeaders() 0 7 1
A addRecipient() 0 15 1
A build() 0 23 3
A checkContentEncryptionAlgorithm() 0 9 3
A processRecipient() 0 16 4
A encryptJWE() 0 17 4
B preparePayload() 0 17 5
A getEncryptedKey() 0 11 4
A getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm() 0 6 1
A getEncryptedKeyFromKeyEncryptionAlgorithm() 0 4 1
A getEncryptedKeyFromKeyWrappingAlgorithm() 0 4 1
A checkKeys() 0 9 2
C determineCEK() 0 29 7
A getCompressionMethod() 0 8 2
A areKeyManagementModesCompatible() 0 14 2
A createCEK() 0 4 1
A createIV() 0 4 1
A getKeyEncryptionAlgorithm() 0 12 3
A getContentEncryptionAlgorithm() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like JWEBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use JWEBuilder, and based on these observations, apply Extract Interface, too.

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 Base64Url\Base64Url;
17
use Jose\Component\Core\JWAManager;
18
use Jose\Component\Core\JWK;
19
use Jose\Component\Core\KeyChecker;
20
use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithmInterface;
21
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyAgreementWrappingInterface;
22
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyEncryptionInterface;
23
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyWrappingInterface;
24
use Jose\Component\Encryption\Algorithm\KeyEncryptionAlgorithmInterface;
25
use Jose\Component\Encryption\Compression\CompressionInterface;
26
use Jose\Component\Encryption\Compression\CompressionManager;
27
28
final class JWEBuilder
29
{
30
    /**
31
     * @var mixed
32
     */
33
    private $payload;
34
35
    /**
36
     * @var string|null
37
     */
38
    private $aad;
39
40
    /**
41
     * @var array
42
     */
43
    private $recipients = [];
44
45
    /**
46
     * @var JWAManager
47
     */
48
    private $keyEncryptionAlgorithmManager;
49
50
    /**
51
     * @var JWAManager
52
     */
53
    private $contentEncryptionAlgorithmManager;
54
55
    /**
56
     * @var CompressionManager
57
     */
58
    private $compressionManager;
59
60
    /**
61
     * @var array
62
     */
63
    private $sharedProtectedHeaders = [];
64
65
    /**
66
     * @var array
67
     */
68
    private $sharedHeaders = [];
69
70
    /**
71
     * @var null|CompressionInterface
72
     */
73
    private $compressionMethod = null;
74
75
    /**
76
     * @var null|ContentEncryptionAlgorithmInterface
77
     */
78
    private $contentEncryptionAlgorithm = null;
79
80
    /**
81
     * @var null|string
82
     */
83
    private $keyManagementMode = null;
84
85
    /**
86
     * JWEBuilder constructor.
87
     *
88
     * @param JWAManager $keyEncryptionAlgorithmManager
89
     * @param JWAManager $contentEncryptionAlgorithmManager
90
     * @param CompressionManager $compressionManager
91
     */
92
    public function __construct(JWAManager $keyEncryptionAlgorithmManager, JWAManager $contentEncryptionAlgorithmManager, CompressionManager $compressionManager)
93
    {
94
        $this->keyEncryptionAlgorithmManager = $keyEncryptionAlgorithmManager;
95
        $this->contentEncryptionAlgorithmManager = $contentEncryptionAlgorithmManager;
96
        $this->compressionManager = $compressionManager;
97
    }
98
99
    /**
100
     * @return string[]
101
     */
102
    public function getSupportedKeyEncryptionAlgorithms(): array
103
    {
104
        return $this->keyEncryptionAlgorithmManager->list();
105
    }
106
107
    /**
108
     * @return string[]
109
     */
110
    public function getSupportedContentEncryptionAlgorithms(): array
111
    {
112
        return $this->contentEncryptionAlgorithmManager->list();
113
    }
114
115
    /**
116
     * @return string[]
117
     */
118
    public function getSupportedCompressionMethods(): array
119
    {
120
        return $this->compressionManager->list();
121
    }
122
123
    /**
124
     * @param mixed $payload
125
     *
126
     * @return JWEBuilder
127
     */
128
    public function withPayload($payload): JWEBuilder
129
    {
130
        $clone = clone $this;
131
        $clone->payload = $payload;
132
133
        return $clone;
134
    }
135
136
    /**
137
     * @param string $aad
138
     *
139
     * @return JWEBuilder
140
     */
141
    public function withAAD(string $aad): JWEBuilder
142
    {
143
        $clone = clone $this;
144
        $clone->aad = $aad;
145
146
        return $clone;
147
    }
148
149
    /**
150
     * @param array $sharedProtectedHeaders
151
     *
152
     * @return JWEBuilder
153
     */
154
    public function withSharedProtectedHeaders(array $sharedProtectedHeaders): JWEBuilder
155
    {
156
        $clone = clone $this;
157
        $clone->sharedProtectedHeaders = $sharedProtectedHeaders;
158
159
        return $clone;
160
    }
161
162
    /**
163
     * @param array $sharedHeaders
164
     *
165
     * @return JWEBuilder
166
     */
167
    public function withSharedHeaders(array $sharedHeaders): JWEBuilder
168
    {
169
        $clone = clone $this;
170
        $clone->sharedHeaders = $sharedHeaders;
171
172
        return $clone;
173
    }
174
175
    /**
176
     * @param JWK $recipientKey
177
     * @param array $recipientHeaders
178
     *
179
     * @return JWEBuilder
180
     */
181
    public function addRecipient(JWK $recipientKey, array $recipientHeaders = []): JWEBuilder
182
    {
183
        $clone = clone $this;
184
        $completeHeaders = array_merge($clone->sharedHeaders, $recipientHeaders, $clone->sharedProtectedHeaders);
185
        $clone->checkContentEncryptionAlgorithm($completeHeaders);
186
        $keyEncryptionAlgorithm = $clone->getKeyEncryptionAlgorithm($completeHeaders);
187
        $clone->checkKeys($keyEncryptionAlgorithm, $clone->contentEncryptionAlgorithm, $recipientKey);
0 ignored issues
show
Bug introduced by
It seems like $clone->contentEncryptionAlgorithm can be null; however, checkKeys() 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);
    }
}
Loading history...
188
        $clone->recipients[] = [
189
            'key' => $recipientKey,
190
            'headers' => $recipientHeaders,
191
            'key_encryption_algorithm' => $keyEncryptionAlgorithm,
192
        ];
193
194
        return $clone;
195
    }
196
197
    /**
198
     * @return JWE
199
     */
200
    public function build(): JWE
201
    {
202
        if (0 === count($this->recipients)) {
203
            throw new \LogicException('No recipient.');
204
        }
205
206
        $additionalHeaders = [];
207
        $this->contentEncryptionAlgorithm = $this->getContentEncryptionAlgorithm($jwe);
0 ignored issues
show
Bug introduced by
The variable $jwe seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
208
        $this->compressionMethod = $this->getCompressionMethod($jwe);
0 ignored issues
show
Bug introduced by
The variable $jwe seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
209
        $this->keyManagementMode = $this->getKeyManagementMode($jwe);
0 ignored issues
show
Bug introduced by
The variable $jwe seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The method getKeyManagementMode() does not seem to exist on object<Jose\Component\Encryption\JWEBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
210
        $cek = $this->determineCEK($jwe, $contentEncryptionAlgorithm, $keyManagementMode, $additionalHeaders);
0 ignored issues
show
Bug introduced by
The variable $jwe seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $contentEncryptionAlgorithm does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Unused Code introduced by
The call to JWEBuilder::determineCEK() has too many arguments starting with $additionalHeaders.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
211
212
        foreach ($this->recipients as $recipient) {
213
            $this->processRecipient($jwe, $jwe->getRecipient($i), $cek, $contentEncryptionAlgorithm, $additionalHeaders);
0 ignored issues
show
Bug introduced by
$jwe->getRecipient($i) cannot be passed to processrecipient() as the parameter $recipient expects a reference.
Loading history...
Bug introduced by
The variable $jwe seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $i does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
214
        }
215
216
        $iv_size = $contentEncryptionAlgorithm->getIVSize();
217
        $iv = $this->createIV($iv_size);
218
219
        $this->encryptJWE($jwe, $contentEncryptionAlgorithm, $cek, $iv, $compressionMethod);
0 ignored issues
show
Bug introduced by
The variable $compressionMethod does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
220
221
        return JWE::createFromLoadedData($this->payload, $this->sharedProtectedHeaders, $this->sharedHeaders, $this->aad);
0 ignored issues
show
Documentation introduced by
$this->sharedProtectedHeaders is of type array, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->sharedHeaders is of type array, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
222
    }
223
224
    /**
225
     * @param array $completeHeaders
226
     */
227
    private function checkContentEncryptionAlgorithm(array $completeHeaders): void
228
    {
229
        $contentEncryptionAlgorithm = $this->getContentEncryptionAlgorithm($completeHeaders);
230
        if (null === $this->contentEncryptionAlgorithm) {
231
            $this->contentEncryptionAlgorithm = $contentEncryptionAlgorithm;
232
        } elseif($contentEncryptionAlgorithm->name() !== $this->contentEncryptionAlgorithm->name()) {
233
            throw new \InvalidArgumentException('Inconsistent content encryption algorithm');
234
        }
235
    }
236
237
    /**
238
     * @param JWE                                 $jwe
239
     * @param Recipient                           $recipient
240
     * @param string                              $cek
241
     * @param ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm
242
     * @param array                               $additionalHeaders
243
     */
244
    private function processRecipient(JWE $jwe, Recipient &$recipient, $cek, ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm, array &$additionalHeaders)
245
    {
246
        if (null === $recipient->getRecipientKey()) {
247
            return;
248
        }
249
        $completeHeaders = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $recipient->getHeaders());
250
        $keyEncryptionAlgorithm = $this->getKeyEncryptionAlgorithm($completeHeaders);
251
        $encrypted_content_encryption_key = $this->getEncryptedKey($completeHeaders, $cek, $keyEncryptionAlgorithm, $contentEncryptionAlgorithm, $additionalHeaders, $recipient->getRecipientKey());
252
        $recipient_headers = $recipient->getHeaders();
253
        if (!empty($additionalHeaders) && 1 !== $jwe->countRecipients()) {
254
            $recipient_headers = array_merge($recipient_headers, $additionalHeaders);
255
            $additionalHeaders = [];
256
        }
257
258
        $recipient = Recipient::createRecipientFromLoadedJWE($recipient_headers, $encrypted_content_encryption_key);
259
    }
260
261
    /**
262
     * @param JWE                                 $jwe
263
     * @param ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm
264
     * @param string                              $cek
265
     * @param string                              $iv
266
     * @param CompressionInterface|null           $compressionMethod
267
     */
268
    private function encryptJWE(JWE &$jwe, ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm, $cek, $iv, CompressionInterface $compressionMethod = null)
269
    {
270
        if (!empty($jwe->getSharedProtectedHeaders())) {
271
            $jwe = $jwe->withEncodedSharedProtectedHeaders(Base64Url::encode(json_encode($jwe->getSharedProtectedHeaders())));
272
        }
273
274
        $tag = null;
275
        $payload = $this->preparePayload($jwe->getPayload(), $compressionMethod);
276
        $aad = null === $jwe->getAAD() ? null : Base64Url::encode($jwe->getAAD());
277
        $ciphertext = $contentEncryptionAlgorithm->encryptContent($payload, $cek, $iv, $aad, $jwe->getEncodedSharedProtectedHeaders(), $tag);
278
        $jwe = $jwe->withCiphertext($ciphertext);
279
        $jwe = $jwe->withIV($iv);
280
281
        if (null !== $tag) {
282
            $jwe = $jwe->withTag($tag);
283
        }
284
    }
285
286
    /**
287
     * @param string                    $payload
288
     * @param CompressionInterface|null $compressionMethod
289
     *
290
     * @return string
291
     */
292
    private function preparePayload($payload, CompressionInterface $compressionMethod = null)
293
    {
294
        $prepared = is_string($payload) ? $payload : json_encode($payload);
295
        if (null === $prepared) {
296
            throw new \RuntimeException('The payload is empty or cannot encoded into JSON.');
297
        }
298
299
        if (null === $compressionMethod) {
300
            return $prepared;
301
        }
302
        $compressedPayload = $compressionMethod->compress($prepared);
303
        if (null === $compressedPayload) {
304
            throw new \RuntimeException('The payload cannot be compressed.');
305
        }
306
307
        return $compressedPayload;
308
    }
309
310
    /**
311
     * @param array                               $completeHeaders
312
     * @param string                              $cek
313
     * @param KeyEncryptionAlgorithmInterface     $keyEncryptionAlgorithm
314
     * @param ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm
315
     * @param JWK                                 $recipientKey
316
     * @param array                               $additionalHeaders
317
     *
318
     * @return string|null
319
     */
320
    private function getEncryptedKey(array $completeHeaders, $cek, KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm, ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm, array &$additionalHeaders, JWK $recipientKey)
321
    {
322
        if ($keyEncryptionAlgorithm instanceof KeyEncryptionInterface) {
323
            return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($completeHeaders, $cek, $keyEncryptionAlgorithm, $recipientKey, $additionalHeaders);
324
        } elseif ($keyEncryptionAlgorithm instanceof KeyWrappingInterface) {
325
            return $this->getEncryptedKeyFromKeyWrappingAlgorithm($completeHeaders, $cek, $keyEncryptionAlgorithm, $recipientKey, $additionalHeaders);
326
        } elseif ($keyEncryptionAlgorithm instanceof KeyAgreementWrappingInterface) {
327
            return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($completeHeaders, $cek, $keyEncryptionAlgorithm, $contentEncryptionAlgorithm, $additionalHeaders, $recipientKey);
328
        }
329
        throw new \InvalidArgumentException('Unsupported key encryption algorithm.');
330
    }
331
332
    /**
333
     * @param array                               $completeHeaders
334
     * @param string                              $cek
335
     * @param KeyAgreementWrappingInterface       $keyEncryptionAlgorithm
336
     * @param ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm
337
     * @param array                               $additionalHeaders
338
     * @param JWK                                 $recipientKey
339
     *
340
     * @return string
341
     */
342
    private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $completeHeaders, $cek, KeyAgreementWrappingInterface $keyEncryptionAlgorithm, ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm, array &$additionalHeaders, JWK $recipientKey)
343
    {
344
        $jwt_cek = $keyEncryptionAlgorithm->wrapAgreementKey($recipientKey, $cek, $contentEncryptionAlgorithm->getCEKSize(), $completeHeaders, $additionalHeaders);
345
346
        return $jwt_cek;
347
    }
348
349
    /**
350
     * @param array                  $completeHeaders
351
     * @param string                 $cek
352
     * @param KeyEncryptionInterface $keyEncryptionAlgorithm
353
     * @param JWK                    $recipientKey
354
     * @param array                  $additionalHeaders
355
     *
356
     * @return string
357
     */
358
    private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $completeHeaders, $cek, KeyEncryptionInterface $keyEncryptionAlgorithm, JWK $recipientKey, array &$additionalHeaders)
359
    {
360
        return $keyEncryptionAlgorithm->encryptKey($recipientKey, $cek, $completeHeaders, $additionalHeaders);
361
    }
362
363
    /**
364
     * @param array                $completeHeaders
365
     * @param string               $cek
366
     * @param KeyWrappingInterface $keyEncryptionAlgorithm
367
     * @param JWK                  $recipientKey
368
     * @param array                $additionalHeaders
369
     *
370
     * @return string
371
     */
372
    private function getEncryptedKeyFromKeyWrappingAlgorithm(array $completeHeaders, $cek, KeyWrappingInterface $keyEncryptionAlgorithm, JWK $recipientKey, &$additionalHeaders)
373
    {
374
        return $keyEncryptionAlgorithm->wrapKey($recipientKey, $cek, $completeHeaders, $additionalHeaders);
375
    }
376
377
    /**
378
     * @param KeyEncryptionAlgorithmInterface     $keyEncryptionAlgorithm
379
     * @param ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm
380
     * @param JWK                                 $recipientKey
381
     */
382
    private function checkKeys(KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm, ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm, JWK $recipientKey)
383
    {
384
        KeyChecker::checkKeyUsage($recipientKey, 'encryption');
385
        if ('dir' !== $keyEncryptionAlgorithm->name()) {
386
            KeyChecker::checkKeyAlgorithm($recipientKey, $keyEncryptionAlgorithm->name());
387
        } else {
388
            KeyChecker::checkKeyAlgorithm($recipientKey, $contentEncryptionAlgorithm->name());
389
        }
390
    }
391
392
    /**
393
     * @param JWE                                 $jwe
394
     * @param ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm
395
     * @param array                               $additionalHeaders
396
     *
397
     * @return string
398
     */
399
    private function determineCEK(JWE $jwe, ContentEncryptionAlgorithmInterface $contentEncryptionAlgorithm, array &$additionalHeaders): string
400
    {
401
        switch ($this->keyManagementMode) {
402
            case KeyEncryptionInterface::MODE_ENCRYPT:
403
            case KeyEncryptionInterface::MODE_WRAP:
404
                return $this->createCEK($contentEncryptionAlgorithm->getCEKSize());
405
            case KeyEncryptionInterface::MODE_AGREEMENT:
406
                if (1 !== count($this->recipients)) {
407
                    throw new \LogicException('Unable to encrypt for multiple recipients using key agreement algorithms.');
408
                }
409
                // Get the algorithm from the recipient directly
410
411
                //$completeHeaders = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $jwe->getRecipient(0)->getHeaders());
412
                //$algorithm = $this->getKeyEncryptionAlgorithm($completeHeaders);
413
414
                return $algorithm->getAgreementKey($contentEncryptionAlgorithm->getCEKSize(), $contentEncryptionAlgorithm->name(), $jwe->getRecipient(0)->getRecipientKey(), $completeHeaders, $additionalHeaders);
0 ignored issues
show
Bug introduced by
The variable $algorithm does not exist. Did you mean $contentEncryptionAlgorithm?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $completeHeaders does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
415
            case KeyEncryptionInterface::MODE_DIRECT:
416
                if (1 !== count($this->recipients)) {
417
                    throw new \LogicException('Unable to encrypt for multiple recipients using key agreement algorithms.');
418
                }
419
420
                Assertion::eq($jwe->getRecipient(0)->getRecipientKey()->get('kty'), 'oct', 'Wrong key type.');
421
                Assertion::true($jwe->getRecipient(0)->getRecipientKey()->has('k'), 'The key parameter "k" is missing.');
422
423
                return Base64Url::decode($jwe->getRecipient(0)->getRecipientKey()->get('k'));
424
            default:
425
                throw new \InvalidArgumentException(sprintf('Unsupported key management mode "%s".', $this->keyManagementMode));
426
        }
427
    }
428
429
    /**
430
     * @param array $completeHeaders
431
     *
432
     * @return CompressionInterface|null
433
     */
434
    private function getCompressionMethod(array $completeHeaders): ?CompressionInterface
435
    {
436
        if (!array_key_exists('zip', $completeHeaders)) {
437
            return null;
438
        }
439
440
        return $this->compressionManager->get($completeHeaders['zip']);
441
    }
442
443
    /**
444
     * @param string $current
445
     * @param string $new
446
     *
447
     * @return bool
448
     */
449
    private function areKeyManagementModesCompatible(string $current, string $new): bool
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
450
    {
451
        $agree = KeyEncryptionAlgorithmInterface::MODE_AGREEMENT;
452
        $dir = KeyEncryptionAlgorithmInterface::MODE_DIRECT;
453
        $enc = KeyEncryptionAlgorithmInterface::MODE_ENCRYPT;
454
        $wrap = KeyEncryptionAlgorithmInterface::MODE_WRAP;
455
        $supportedKeyManagementModeCombinations = [$enc.$enc => true, $enc.$wrap => true, $wrap.$enc => true, $wrap.$wrap => true, $agree.$agree => false, $agree.$dir => false, $agree.$enc => false, $agree.$wrap => false, $dir.$agree => false, $dir.$dir => false, $dir.$enc => false, $dir.$wrap => false, $enc.$agree => false, $enc.$dir => false, $wrap.$agree => false, $wrap.$dir => false];
456
457
        if (array_key_exists($current.$new, $supportedKeyManagementModeCombinations)) {
458
            return $supportedKeyManagementModeCombinations[$current.$new];
459
        }
460
461
        return false;
462
    }
463
464
    /**
465
     * @param int $size
466
     *
467
     * @return string
468
     */
469
    private function createCEK(int $size): string
470
    {
471
        return random_bytes($size / 8);
472
    }
473
474
    /**
475
     * @param int $size
476
     *
477
     * @return string
478
     */
479
    private function createIV(int $size): string
480
    {
481
        return random_bytes($size / 8);
482
    }
483
484
    /**
485
     * @param array $completeHeaders
486
     *
487
     * @return KeyEncryptionAlgorithmInterface
488
     */
489
    private function getKeyEncryptionAlgorithm(array $completeHeaders): KeyEncryptionAlgorithmInterface
490
    {
491
        if (!array_key_exists('alg', $completeHeaders)) {
492
            throw new \LogicException('Parameter "alg" is missing.');
493
        }
494
        $keyEncryptionAlgorithm = $this->keyEncryptionAlgorithmManager->get($completeHeaders['alg']);
495
        if (!$keyEncryptionAlgorithm instanceof KeyEncryptionAlgorithmInterface) {
496
            throw new \InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $completeHeaders['alg']));
497
        }
498
499
        return $keyEncryptionAlgorithm;
500
    }
501
502
    /**
503
     * @param array $completeHeaders
504
     *
505
     * @return ContentEncryptionAlgorithmInterface
506
     */
507
    private function getContentEncryptionAlgorithm(array $completeHeaders): ContentEncryptionAlgorithmInterface
508
    {
509
        if (!array_key_exists('enc', $completeHeaders)) {
510
            throw new \LogicException('Parameter "enc" is missing.');
511
        }
512
        $contentEncryptionAlgorithm = $this->contentEncryptionAlgorithmManager->get($completeHeaders['enc']);
513
        if (!$contentEncryptionAlgorithm instanceof ContentEncryptionAlgorithmInterface) {
514
            throw new \InvalidArgumentException(sprintf('The content encryption algorithm "%s" is not supported or not a content encryption algorithm instance.', $completeHeaders['alg']));
515
        }
516
517
        return $contentEncryptionAlgorithm;
518
    }
519
}
520