Completed
Push — master ( 00727d...5aa01a )
by Florent
02:25
created

Encrypter::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 19
rs 9.4285
cc 2
eloc 14
nc 2
nop 4
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
use Jose\Algorithm\ContentEncryptionAlgorithmInterface;
17
use Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface;
18
use Jose\Algorithm\KeyEncryption\KeyEncryptionInterface;
19
use Jose\Algorithm\KeyEncryption\KeyWrappingInterface;
20
use Jose\Algorithm\KeyEncryptionAlgorithmInterface;
21
use Jose\Behaviour\CommonCipheringMethods;
22
use Jose\Behaviour\HasCompressionManager;
23
use Jose\Behaviour\HasJWAManager;
24
use Jose\Behaviour\HasKeyChecker;
25
use Jose\Behaviour\HasLogger;
26
use Jose\Compression\CompressionInterface;
27
use Jose\Factory\AlgorithmManagerFactory;
28
use Jose\Factory\CompressionManagerFactory;
29
use Jose\Object\JWEInterface;
30
use Jose\Object\JWKInterface;
31
use Jose\Object\Recipient;
32
use Jose\Object\RecipientInterface;
33
use Psr\Log\LoggerInterface;
34
use Psr\Log\LogLevel;
35
36
final class Encrypter implements EncrypterInterface
37
{
38
    use HasKeyChecker;
39
    use HasJWAManager;
40
    use HasCompressionManager;
41
    use HasLogger;
42
    use CommonCipheringMethods;
43
    
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public static function createEncrypter(array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF', 'ZLIB', 'GZ'], LoggerInterface $logger = null)
48
    {
49
        return new self($key_encryption_algorithms, $content_encryption_algorithms, $compression_methods, $logger);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new self($key_enc...sion_methods, $logger); (Jose\Encrypter) is incompatible with the return type declared by the interface Jose\EncrypterInterface::createEncrypter of type Jose\DecrypterInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
50
    }
51
52
    /**
53
     * Decrypter constructor.
54
     *
55
     * @param string[]|\Jose\Algorithm\JWAInterface[]           $key_encryption_algorithms
56
     * @param string[]|\Jose\Algorithm\JWAInterface[]           $content_encryption_algorithms
57
     * @param string[]|\Jose\Compression\CompressionInterface[] $compression_methods
58
     * @param \Psr\Log\LoggerInterface|null                     $logger
59
     */
60
    public function __construct(
61
        array $key_encryption_algorithms,
62
        array $content_encryption_algorithms,
63
        array $compression_methods,
64
        LoggerInterface $logger = null
65
    ) {
66
        $this->setKeyEncryptionAlgorithms($key_encryption_algorithms);
0 ignored issues
show
Documentation introduced by
$key_encryption_algorithms is of type array<integer,string|obj...lgorithm\JWAInterface>>, but the function expects a array<integer,string|obj...ionAlgorithmInterface>>.

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...
67
        $this->setContentEncryptionAlgorithms($content_encryption_algorithms);
0 ignored issues
show
Documentation introduced by
$content_encryption_algorithms is of type array<integer,string|obj...lgorithm\JWAInterface>>, but the function expects a array<integer,string|obj...ionAlgorithmInterface>>.

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...
68
        $this->setCompressionMethods($compression_methods);
69
        $this->setJWAManager(AlgorithmManagerFactory::createAlgorithmManager(array_merge(
70
            $key_encryption_algorithms,
71
            $content_encryption_algorithms
72
        )));
73
        $this->setCompressionManager(CompressionManagerFactory::createCompressionManager($compression_methods));
74
75
        if (null !== $logger) {
76
            $this->setLogger($logger);
77
        }
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function encrypt(JWEInterface &$jwe)
84
    {
85
        $this->log(LogLevel::INFO, 'Trying to encrypt the JWE object', ['jwe' => $jwe]);
86
        Assertion::false($jwe->isEncrypted(), 'The JWE is already encrypted.');
87
        Assertion::greaterThan($jwe->countRecipients(), 0, 'The JWE does not contain recipient.');
88
89
        // Content Encryption Algorithm
90
        $this->log(LogLevel::DEBUG, 'Trying to find the content encryption algorithm');
91
        $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($jwe);
92
        $this->log(LogLevel::DEBUG, 'The content encryption algorithm has been found', ['content_encryption_algorithm', $content_encryption_algorithm]);
93
94
        // Compression Method
95
        $this->log(LogLevel::DEBUG, 'Trying to find the compression method (if needed)');
96
        $compression_method = $this->getCompressionMethod($jwe);
97
        $this->log(LogLevel::DEBUG, 'The compression method search is finished', ['compression_method', $compression_method]);
98
99
        // Key Management Mode
100
        $this->log(LogLevel::DEBUG, 'Trying to find the key management mode');
101
        $key_management_mode = $this->getKeyManagementMode($jwe);
102
        $this->log(LogLevel::DEBUG, 'The key management mode has been found', ['key_management_mode', $key_management_mode]);
103
104
        // Additional Headers
105
        $additional_headers = [];
106
107
        // CEK
108
        $this->log(LogLevel::DEBUG, 'Trying to determine the content encryption key (CEK)');
109
        $cek = $this->determineCEK(
110
            $jwe,
111
            $content_encryption_algorithm,
112
            $key_management_mode,
113
            $additional_headers
114
        );
115
        $this->log(LogLevel::DEBUG, 'The content encryption key has been determined', ['cek', $cek]);
116
117
        $nb_recipients = $jwe->countRecipients();
118
119
        $this->log(LogLevel::DEBUG, 'Trying to encrypt the content encryption key (CEK) for all recipients');
120
        for ($i = 0; $i < $nb_recipients; $i++) {
121
            $this->log(LogLevel::DEBUG, 'Processing with recipient #{index}', ['index', $i]);
122
            $this->processRecipient(
123
                $jwe,
124
                $jwe->getRecipient($i),
125
                $cek,
126
                $content_encryption_algorithm,
127
                $additional_headers
128
            );
129
            $this->log(LogLevel::DEBUG, 'Processing done');
130
        }
131
132
        if (!empty($additional_headers) && 1 === $jwe->countRecipients()) {
133
            $this->log(LogLevel::DEBUG, 'Additional headers will be added to the shared protected headers', ['additional_headers' => $additional_headers]);
134
            $jwe = $jwe->withSharedProtectedHeaders(array_merge(
135
                $jwe->getSharedProtectedHeaders(),
136
                $additional_headers
137
            ));
138
            $this->log(LogLevel::DEBUG, 'Additional headers added');
139
        }
140
141
        // IV
142
        $this->log(LogLevel::DEBUG, 'Creating Initialization Vector (IV)');
143
        $iv_size = $content_encryption_algorithm->getIVSize();
144
        $iv = $this->createIV($iv_size);
145
        $this->log(LogLevel::DEBUG, 'Initialization Vector (IV) creation done', ['iv' => $iv]);
146
147
        $this->log(LogLevel::DEBUG, 'Trying to encrypt the JWE object ');
148
        $this->encryptJWE($jwe, $content_encryption_algorithm, $cek, $iv, $compression_method);
149
        $this->log(LogLevel::DEBUG, 'JWE object encryption done.');
150
    }
151
152
    /**
153
     * @param \Jose\Object\JWEInterface                           $jwe
154
     * @param \Jose\Object\RecipientInterface                     $recipient
155
     * @param string                                              $cek
156
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
157
     * @param array                                               $additional_headers
158
     */
159
    private function processRecipient(JWEInterface $jwe,
160
                                      RecipientInterface &$recipient,
161
                                      $cek,
162
                                      ContentEncryptionAlgorithmInterface $content_encryption_algorithm,
163
                                      array &$additional_headers
164
    ) {
165
        if (null === $recipient->getRecipientKey()) {
166
            $this->log(LogLevel::WARNING, 'The recipient key is not set. Aborting.');
167
            return;
168
        }
169
        $complete_headers = array_merge(
170
            $jwe->getSharedProtectedHeaders(),
171
            $jwe->getSharedHeaders(),
172
            $recipient->getHeaders()
173
        );
174
175
        $this->log(LogLevel::DEBUG, 'Trying to find the key encryption algorithm');
176
        $key_encryption_algorithm = $this->findKeyEncryptionAlgorithm($complete_headers);
177
        $this->log(LogLevel::DEBUG, 'The key encryption algorithm has been found', ['key_encryption_algorithm' => $key_encryption_algorithm]);
178
179
        // We check keys (usage and algorithm if restrictions are set)
180
        $this->log(LogLevel::DEBUG, 'Checking recipient key usage');
181
        $this->checkKeys(
182
            $key_encryption_algorithm,
183
            $content_encryption_algorithm,
184
            $recipient->getRecipientKey()
185
        );
186
        $this->log(LogLevel::DEBUG, 'Recipient key usage checks done');
187
188
        $this->log(LogLevel::DEBUG, 'Trying to compute the content encryption key');
189
        $encrypted_content_encryption_key = $this->getEncryptedKey(
190
            $complete_headers,
191
            $cek,
192
            $key_encryption_algorithm,
193
            $content_encryption_algorithm,
194
            $additional_headers,
195
            $recipient->getRecipientKey()
196
        );
197
        $this->log(LogLevel::DEBUG, 'Content encryption key computation done', ['encrypted_content_encryption_key' => $encrypted_content_encryption_key]);
198
199
        $recipient_headers = $recipient->getHeaders();
200
        if (!empty($additional_headers) && 1 !== $jwe->countRecipients()) {
201
            $recipient_headers = array_merge(
202
                $recipient_headers,
203
                $additional_headers
204
            );
205
            $additional_headers = [];
206
        }
207
208
        $recipient = Recipient::createRecipientFromLoadedJWE($recipient_headers, $encrypted_content_encryption_key);
209
    }
210
211
    /**
212
     * @param \Jose\Object\JWEInterface                           $jwe
213
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
214
     * @param string                                              $key_management_mode
215
     * @param array                                               $additional_headers
216
     *
217
     * @return string
218
     */
219
    private function determineCEK(JWEInterface $jwe,
220
                                  ContentEncryptionAlgorithmInterface $content_encryption_algorithm,
221
                                  $key_management_mode,
222
                                  array &$additional_headers
223
    ) {
224
        switch ($key_management_mode) {
225
            case KeyEncryptionInterface::MODE_ENCRYPT:
226
            case KeyEncryptionInterface::MODE_WRAP:
227
                return $this->createCEK($content_encryption_algorithm->getCEKSize());
228
            case KeyEncryptionInterface::MODE_AGREEMENT:
229
                Assertion::eq(1, $jwe->countRecipients(), 'Unable to encrypt for multiple recipients using key agreement algorithms.');
230
231
                $complete_headers = array_merge(
232
                    $jwe->getSharedProtectedHeaders(),
233
                    $jwe->getSharedHeaders(),
234
                    $jwe->getRecipient(0)->getHeaders()
235
                );
236
                $algorithm = $this->findKeyEncryptionAlgorithm($complete_headers);
237
238
                return $algorithm->getAgreementKey($content_encryption_algorithm->getCEKSize(), $content_encryption_algorithm->getAlgorithmName(), $jwe->getRecipient(0)->getRecipientKey(), $complete_headers, $additional_headers);
239
            case KeyEncryptionInterface::MODE_DIRECT:
240
                Assertion::eq(1, $jwe->countRecipients(), 'Unable to encrypt for multiple recipients using key agreement algorithms.');
241
242
                Assertion::eq($jwe->getRecipient(0)->getRecipientKey()->get('kty'), 'oct', 'Wrong key type.');
243
                Assertion::true($jwe->getRecipient(0)->getRecipientKey()->has('k'), 'The key parameter "k" is missing.');
244
245
                return Base64Url::decode($jwe->getRecipient(0)->getRecipientKey()->get('k'));
246
            default:
247
                throw new \InvalidArgumentException(sprintf('Unsupported key management mode "%s".', $key_management_mode));
248
        }
249
    }
250
251
    /**
252
     * @param \Jose\Object\JWEInterface $jwe
253
     *
254
     * @return string
255
     */
256
    private function getKeyManagementMode(JWEInterface $jwe)
257
    {
258
        $mode = null;
259
        $recipients = $jwe->getRecipients();
260
        
261
        foreach ($recipients as $recipient) {
262
            $complete_headers = array_merge(
263
                $jwe->getSharedProtectedHeaders(),
264
                $jwe->getSharedHeaders(),
265
                $recipient->getHeaders()
266
            );
267
            Assertion::keyExists($complete_headers, 'alg', 'Parameter "alg" is missing.');
268
269
            $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']);
270
            Assertion::isInstanceOf($key_encryption_algorithm, KeyEncryptionAlgorithmInterface::class, sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $complete_headers['alg']));
271
272
            if (null === $mode) {
273
                $mode = $key_encryption_algorithm->getKeyManagementMode();
274
            } else {
275
                Assertion::true(
276
                    $this->areKeyManagementModesCompatible($mode, $key_encryption_algorithm->getKeyManagementMode()),
277
                    'Foreign key management mode forbidden.'
278
                );
279
            }
280
        }
281
282
        return $mode;
283
    }
284
285
    /**
286
     * @param \Jose\Object\JWEInterface $jwe
287
     *
288
     * @return \Jose\Compression\CompressionInterface|null
289
     */
290
    private function getCompressionMethod(JWEInterface $jwe)
291
    {
292
        $method = null;
293
        $nb_recipients = $jwe->countRecipients();
294
295
        for ($i = 0; $i < $nb_recipients; $i++) {
296
            $complete_headers = array_merge(
297
                $jwe->getSharedProtectedHeaders(),
298
                $jwe->getSharedHeaders(),
299
                $jwe->getRecipient($i)->getHeaders()
300
            );
301
            if (array_key_exists('zip', $complete_headers)) {
302
                if (null === $method) {
303
                    if (0 === $i) {
304
                        $method = $complete_headers['zip'];
305
                    } else {
306
                        throw new \InvalidArgumentException('Inconsistent "zip" parameter.');
307
                    }
308
                } else {
309
                    Assertion::eq($method, $complete_headers['zip'], 'Inconsistent "zip" parameter.');
310
                }
311
            } else {
312
                Assertion::eq(null, $method, 'Inconsistent "zip" parameter.');
313
            }
314
        }
315
316
        if (null === $method) {
317
            return;
318
        }
319
320
        $compression_method = $this->getCompressionManager()->getCompressionAlgorithm($method);
321
        Assertion::isInstanceOf($compression_method, CompressionInterface::class, sprintf('Compression method "%s" not supported.', $method));
322
323
        return $compression_method;
324
    }
325
326
    /**
327
     * @param \Jose\Object\JWEInterface $jwe
328
     *
329
     * @return \Jose\Algorithm\ContentEncryptionAlgorithmInterface
330
     */
331
    private function getContentEncryptionAlgorithm(JWEInterface $jwe)
332
    {
333
        $algorithm = null;
334
335
        foreach ($jwe->getRecipients() as $recipient) {
336
            $complete_headers = array_merge(
337
                $jwe->getSharedProtectedHeaders(),
338
                $jwe->getSharedHeaders(),
339
                $recipient->getHeaders()
340
            );
341
            Assertion::keyExists($complete_headers, 'enc', 'Parameter "enc" is missing.');
342
            if (null === $algorithm) {
343
                $algorithm = $complete_headers['enc'];
344
            } else {
345
                Assertion::eq($algorithm, $complete_headers['enc'], 'Foreign content encryption algorithms are not allowed.');
346
            }
347
        }
348
349
        $content_encryption_algorithm = $this->getJWAManager()->getAlgorithm($algorithm);
350
        Assertion::isInstanceOf($content_encryption_algorithm, ContentEncryptionAlgorithmInterface::class, sprintf('The content encryption algorithm "%s" is not supported or not a content encryption algorithm instance.', $algorithm));
351
352
        return $content_encryption_algorithm;
353
    }
354
355
    /**
356
     * @param \Jose\Object\JWEInterface                           $jwe
357
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
358
     * @param string                                              $cek
359
     * @param string                                              $iv
360
     * @param \Jose\Compression\CompressionInterface|null         $compression_method
361
     */
362
    private function encryptJWE(JWEInterface &$jwe,
363
                                ContentEncryptionAlgorithmInterface $content_encryption_algorithm,
364
                                $cek,
365
                                $iv,
366
                                CompressionInterface $compression_method = null
367
    ) {
368
        if (!empty($jwe->getSharedProtectedHeaders())) {
369
            $jwe = $jwe->withEncodedSharedProtectedHeaders(Base64Url::encode(json_encode($jwe->getSharedProtectedHeaders())));
370
        }
371
372
        // We encrypt the payload and get the tag
373
        $tag = null;
374
        $payload = $this->preparePayload($jwe->getPayload(), $compression_method);
375
376
        $ciphertext = $content_encryption_algorithm->encryptContent(
377
            $payload,
378
            $cek,
379
            $iv,
380
            null === $jwe->getAAD() ? null : Base64Url::encode($jwe->getAAD()),
381
            $jwe->getEncodedSharedProtectedHeaders(),
382
            $tag
383
        );
384
385
        $jwe = $jwe->withCiphertext($ciphertext);
386
        $jwe = $jwe->withIV($iv);
387
388
        // Tag
389
        if (null !== $tag) {
390
            $jwe = $jwe->withTag($tag);
391
        }
392
    }
393
394
    /**
395
     * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface     $key_encryption_algorithm
396
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
397
     * @param \Jose\Object\JWKInterface                           $recipient_key
398
     */
399
    private function checkKeys(KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, JWKInterface $recipient_key)
400
    {
401
        $this->checkKeyUsage($recipient_key, 'encryption');
402
        if ('dir' !== $key_encryption_algorithm->getAlgorithmName()) {
403
            $this->checkKeyAlgorithm($recipient_key, $key_encryption_algorithm->getAlgorithmName());
404
        } else {
405
            $this->checkKeyAlgorithm($recipient_key, $content_encryption_algorithm->getAlgorithmName());
406
        }
407
    }
408
409
    /**
410
     * @param string $current
411
     * @param string $new
412
     *
413
     * @return bool
414
     */
415
    private function areKeyManagementModesCompatible($current, $new)
416
    {
417
        $agree = KeyEncryptionAlgorithmInterface::MODE_AGREEMENT;
418
        $dir = KeyEncryptionAlgorithmInterface::MODE_DIRECT;
419
        $enc = KeyEncryptionAlgorithmInterface::MODE_ENCRYPT;
420
        $wrap = KeyEncryptionAlgorithmInterface::MODE_WRAP;
421
422
        $supported_key_management_mode_combinations = [
423
            $enc.$enc     => true,
424
            $enc.$wrap    => true,
425
            $wrap.$enc    => true,
426
            $wrap.$wrap   => true,
427
            $agree.$agree => false,
428
            $agree.$dir   => false,
429
            $agree.$enc   => false,
430
            $agree.$wrap  => false,
431
            $dir.$agree   => false,
432
            $dir.$dir     => false,
433
            $dir.$enc     => false,
434
            $dir.$wrap    => false,
435
            $enc.$agree   => false,
436
            $enc.$dir     => false,
437
            $wrap.$agree  => false,
438
            $wrap.$dir    => false,
439
        ];
440
441
        if (array_key_exists($current.$new, $supported_key_management_mode_combinations)) {
442
            return $supported_key_management_mode_combinations[$current.$new];
443
        }
444
445
        return false;
446
    }
447
448
    /**
449
     * @param string                                      $payload
450
     * @param \Jose\Compression\CompressionInterface|null $compression_method
451
     *
452
     * @return string
453
     */
454
    private function preparePayload($payload, CompressionInterface $compression_method = null)
455
    {
456
        $prepared = is_string($payload) ? $payload : json_encode($payload);
457
458
        Assertion::notNull($prepared, 'The payload is empty or cannot encoded into JSON.');
459
460
        if (null === $compression_method) {
461
            return $prepared;
462
        }
463
        $compressed_payload = $compression_method->compress($prepared);
464
        Assertion::string($compressed_payload, 'Compression failed.');
465
466
        return $compressed_payload;
467
    }
468
469
    /**
470
     * @param array                                               $complete_headers
471
     * @param string                                              $cek
472
     * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface     $key_encryption_algorithm
473
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
474
     * @param \Jose\Object\JWKInterface                           $recipient_key
475
     * @param array                                               $additional_headers
476
     *
477
     * @return string|null
478
     */
479
    private function getEncryptedKey(array $complete_headers, $cek, KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, JWKInterface $recipient_key)
480
    {
481
        if ($key_encryption_algorithm instanceof KeyEncryptionInterface) {
482
            return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers);
483
        } elseif ($key_encryption_algorithm instanceof KeyWrappingInterface) {
484
            return $this->getEncryptedKeyFromKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers);
485
        } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) {
486
            return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $content_encryption_algorithm, $additional_headers, $recipient_key);
487
        }
488
489
        // Using KeyAgreementInterface or DirectEncryptionInterface, the encrypted key is an empty string
490
    }
491
492
    /**
493
     * @param array                                                       $complete_headers
494
     * @param string                                                      $cek
495
     * @param \Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface $key_encryption_algorithm
496
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface         $content_encryption_algorithm
497
     * @param array                                                       $additional_headers
498
     * @param \Jose\Object\JWKInterface                                   $recipient_key
499
     *
500
     * @return string
501
     */
502
    private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $complete_headers, $cek, KeyAgreementWrappingInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, JWKInterface $recipient_key)
503
    {
504
        $jwt_cek = $key_encryption_algorithm->wrapAgreementKey($recipient_key, $cek, $content_encryption_algorithm->getCEKSize(), $complete_headers, $additional_headers);
505
506
        return $jwt_cek;
507
    }
508
509
    /**
510
     * @param array                                                $complete_headers
511
     * @param string                                               $cek
512
     * @param \Jose\Algorithm\KeyEncryption\KeyEncryptionInterface $key_encryption_algorithm
513
     * @param \Jose\Object\JWKInterface                            $recipient_key
514
     * @param array                                                $additional_headers
515
     *
516
     * @return string
517
     */
518
    private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $complete_headers, $cek, KeyEncryptionInterface $key_encryption_algorithm, JWKInterface $recipient_key, array &$additional_headers)
519
    {
520
        return $key_encryption_algorithm->encryptKey(
521
            $recipient_key,
522
            $cek,
523
            $complete_headers,
524
            $additional_headers
525
        );
526
    }
527
528
    /**
529
     * @param array                                              $complete_headers
530
     * @param string                                             $cek
531
     * @param \Jose\Algorithm\KeyEncryption\KeyWrappingInterface $key_encryption_algorithm
532
     * @param \Jose\Object\JWKInterface                          $recipient_key
533
     * @param array                                              $additional_headers
534
     *
535
     * @return string
536
     */
537
    private function getEncryptedKeyFromKeyWrappingAlgorithm(array $complete_headers, $cek, KeyWrappingInterface $key_encryption_algorithm, JWKInterface $recipient_key, &$additional_headers)
538
    {
539
        return $key_encryption_algorithm->wrapKey(
540
            $recipient_key,
541
            $cek,
542
            $complete_headers,
543
            $additional_headers
544
        );
545
    }
546
547
    /**
548
     * @param array $complete_headers
549
     *
550
     * @return \Jose\Algorithm\KeyEncryptionAlgorithmInterface
551
     */
552
    private function findKeyEncryptionAlgorithm(array $complete_headers)
553
    {
554
        Assertion::keyExists($complete_headers, 'alg', 'Parameter "alg" is missing.');
555
556
        $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']);
557
        Assertion::isInstanceOf($key_encryption_algorithm, KeyEncryptionAlgorithmInterface::class, sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $complete_headers['alg']));
558
559
        return $key_encryption_algorithm;
560
    }
561
562
    /**
563
     * @param int $size
564
     *
565
     * @return string
566
     */
567
    private function createCEK($size)
568
    {
569
        return random_bytes($size / 8);
570
    }
571
572
    /**
573
     * @param int $size
574
     *
575
     * @return string
576
     */
577
    private function createIV($size)
578
    {
579
        return random_bytes($size / 8);
580
    }
581
}
582