Completed
Push — develop ( 062dde...f3af82 )
by Florent
02:29
created

Encrypter::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 12
rs 9.4285
cc 2
eloc 8
nc 2
nop 3
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\JWAManagerInterface;
18
use Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface;
19
use Jose\Algorithm\KeyEncryption\KeyEncryptionInterface;
20
use Jose\Algorithm\KeyEncryption\KeyWrappingInterface;
21
use Jose\Algorithm\KeyEncryptionAlgorithmInterface;
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\Compression\CompressionManagerInterface;
28
use Jose\Object\JWEInterface;
29
use Jose\Object\JWKInterface;
30
use Jose\Object\RecipientInterface;
31
use Jose\Util\StringUtil;
32
use Psr\Log\LoggerInterface;
33
34
final class Encrypter implements EncrypterInterface
35
{
36
    use HasKeyChecker;
37
    use HasJWAManager;
38
    use HasCompressionManager;
39
    use HasLogger;
40
41
    /**
42
     * Encrypter constructor.
43
     *
44
     * @param \Jose\Algorithm\JWAManagerInterface           $jwa_manager
45
     * @param \Jose\Compression\CompressionManagerInterface $compression_manager
46
     * @param \Psr\Log\LoggerInterface|null                 $logger
47
     */
48
    public function __construct(
49
        JWAManagerInterface $jwa_manager,
50
        CompressionManagerInterface $compression_manager,
51
        LoggerInterface $logger = null
52
    ) {
53
        $this->setJWAManager($jwa_manager);
54
        $this->setCompressionManager($compression_manager);
55
56
        if (null !== $logger) {
57
            $this->setLogger($logger);
58
        }
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function encrypt(JWEInterface &$jwe)
65
    {
66
        Assertion::greaterThan($jwe->countRecipients(), 0, 'The JWE does not contain recipient.');
67
68
        // Content Encryption Algorithm
69
        $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($jwe);
70
71
        // Compression Method
72
        $compression_method = $this->getCompressionMethod($jwe);
73
74
        // Key Management Mode
75
        $key_management_mode = $this->getKeyManagementMode($jwe);
76
77
        // Additional Headers
78
        $additional_headers = [];
79
80
        // CEK
81
        $cek = $this->determineCEK(
82
            $jwe,
83
            $content_encryption_algorithm,
0 ignored issues
show
Bug introduced by
It seems like $content_encryption_algorithm defined by $this->getContentEncryptionAlgorithm($jwe) on line 69 can be null; however, Jose\Encrypter::determineCEK() 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...
84
            $key_management_mode,
85
            $additional_headers
86
        );
87
88
        for ($i = 0; $i < $jwe->countRecipients(); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
89
            $this->processRecipient(
90
                $jwe,
91
                $jwe->getRecipient($i),
92
                $cek,
93
                $content_encryption_algorithm,
0 ignored issues
show
Bug introduced by
It seems like $content_encryption_algorithm defined by $this->getContentEncryptionAlgorithm($jwe) on line 69 can be null; however, Jose\Encrypter::processRecipient() 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...
94
                $additional_headers
95
            );
96
        }
97
98
        if (!empty($additional_headers) && 1 === $jwe->countRecipients()) {
99
            $jwe = $jwe->withSharedProtectedHeaders(array_merge(
100
                $jwe->getSharedProtectedHeaders(),
101
                $additional_headers
102
            ));
103
        }
104
105
        // IV
106
        if (null !== $iv_size = $content_encryption_algorithm->getIVSize()) {
107
            $iv = $this->createIV($iv_size);
108
            $jwe = $jwe->withIV($iv);
109
        }
110
111
        $jwe = $jwe->withContentEncryptionKey($cek);
112
113
        $this->encryptJWE($jwe, $content_encryption_algorithm, $compression_method);
0 ignored issues
show
Bug introduced by
It seems like $content_encryption_algorithm defined by $this->getContentEncryptionAlgorithm($jwe) on line 69 can be null; however, Jose\Encrypter::encryptJWE() 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...
114
    }
115
116
    /**
117
     * @param \Jose\Object\JWEInterface                           $jwe
118
     * @param \Jose\Object\RecipientInterface                     $recipient
119
     * @param string                                              $cek
120
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
121
     * @param array                                               $additional_headers
122
     */
123
    private function processRecipient(JWEInterface $jwe,
124
                                      RecipientInterface &$recipient,
125
                                      $cek,
126
                                      ContentEncryptionAlgorithmInterface $content_encryption_algorithm,
127
                                      array &$additional_headers
128
    ) {
129
        $complete_headers = array_merge(
130
            $jwe->getSharedProtectedHeaders(),
131
            $jwe->getSharedHeaders(),
132
            $recipient->getHeaders()
133
        );
134
135
        $key_encryption_algorithm = $this->findKeyEncryptionAlgorithm($complete_headers);
136
137
        // We check keys (usage and algorithm if restrictions are set)
138
        $this->checkKeys(
139
            $key_encryption_algorithm,
0 ignored issues
show
Bug introduced by
It seems like $key_encryption_algorithm defined by $this->findKeyEncryption...ithm($complete_headers) on line 135 can be null; however, Jose\Encrypter::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...
140
            $content_encryption_algorithm,
141
            $recipient->getRecipientKey()
142
        );
143
144
        $encrypted_content_encryption_key = $this->getEncryptedKey(
145
            $complete_headers,
146
            $cek,
147
            $key_encryption_algorithm,
0 ignored issues
show
Bug introduced by
It seems like $key_encryption_algorithm defined by $this->findKeyEncryption...ithm($complete_headers) on line 135 can be null; however, Jose\Encrypter::getEncryptedKey() 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...
148
            $content_encryption_algorithm,
149
            $additional_headers,
150
            $recipient->getRecipientKey()
151
        );
152
153
        if (!empty($additional_headers) && 1 !== $jwe->countRecipients()) {
154
            foreach ($additional_headers as $key => $value) {
155
                $recipient = $recipient->withHeader($key, $value);
156
            }
157
        }
158
        if (null !== $encrypted_content_encryption_key) {
159
            $recipient = $recipient->withEncryptedKey($encrypted_content_encryption_key);
160
        }
161
    }
162
163
    /**
164
     * @param \Jose\Object\JWEInterface                           $jwe
165
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
166
     * @param string                                              $key_management_mode
167
     * @param array                                               $additional_headers
168
     *
169
     * @return string
170
     */
171
    private function determineCEK(JWEInterface $jwe,
172
                                  ContentEncryptionAlgorithmInterface $content_encryption_algorithm,
173
                                  $key_management_mode,
174
                                  array &$additional_headers
175
    ) {
176
        switch ($key_management_mode) {
177
            case KeyEncryptionInterface::MODE_ENCRYPT:
178
            case KeyEncryptionInterface::MODE_WRAP:
179
                return $this->createCEK($content_encryption_algorithm->getCEKSize());
180
            case KeyEncryptionInterface::MODE_AGREEMENT:
181
                Assertion::eq(1, $jwe->countRecipients(), 'Unable to encrypt for multiple recipients using key agreement algorithms.');
182
183
                $complete_headers = array_merge(
184
                    $jwe->getSharedProtectedHeaders(),
185
                    $jwe->getSharedHeaders(),
186
                    $jwe->getRecipient(0)->getHeaders()
187
                );
188
                $algorithm = $this->findKeyEncryptionAlgorithm($complete_headers);
189
190
                return $algorithm->getAgreementKey($content_encryption_algorithm->getCEKSize(), $content_encryption_algorithm->getAlgorithmName(), $jwe->getRecipient(0)->getRecipientKey(), $complete_headers, $additional_headers);
191
            case KeyEncryptionInterface::MODE_DIRECT:
192
                Assertion::eq(1, $jwe->countRecipients(), 'Unable to encrypt for multiple recipients using key agreement algorithms.');
193
194
                Assertion::eq($jwe->getRecipient(0)->getRecipientKey()->get('kty'), 'oct', 'Wrong key type.');
195
                Assertion::true($jwe->getRecipient(0)->getRecipientKey()->has('k'), 'The key parameter "k" is missing.');
196
197
                return Base64Url::decode($jwe->getRecipient(0)->getRecipientKey()->get('k'));
198
            default:
199
                throw new \InvalidArgumentException(sprintf('Unsupported key management mode "%s".', $key_management_mode));
200
        }
201
    }
202
203
    /**
204
     * @param \Jose\Object\JWEInterface $jwe
205
     *
206
     * @return string
207
     */
208
    private function getKeyManagementMode(JWEInterface $jwe)
209
    {
210
        $mode = null;
211
212
        foreach ($jwe->getRecipients() as $recipient) {
213
            $complete_headers = array_merge(
214
                $jwe->getSharedProtectedHeaders(),
215
                $jwe->getSharedHeaders(),
216
                $recipient->getHeaders()
217
            );
218
            Assertion::keyExists($complete_headers, 'alg', 'Parameter "alg" is missing.');
219
220
            $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']);
221
            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']));
222
223
            if (null === $mode) {
224
                $mode = $key_encryption_algorithm->getKeyManagementMode();
225
            } else {
226
                Assertion::true(
227
                    $this->areKeyManagementModesCompatible($mode, $key_encryption_algorithm->getKeyManagementMode()),
228
                    'Foreign key management mode forbidden.'
229
                );
230
            }
231
        }
232
233
        return $mode;
234
    }
235
236
    /**
237
     * @param \Jose\Object\JWEInterface $jwe
238
     *
239
     * @return \Jose\Algorithm\ContentEncryptionAlgorithmInterface
240
     */
241
    private function getCompressionMethod(JWEInterface $jwe)
242
    {
243
        $method = null;
244
245
        for ($i = 0; $i < $jwe->countRecipients(); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
246
            $complete_headers = array_merge(
247
                $jwe->getSharedProtectedHeaders(),
248
                $jwe->getSharedHeaders(),
249
                $jwe->getRecipient($i)->getHeaders()
250
            );
251
            if (array_key_exists('zip', $complete_headers)) {
252
                if (null === $method) {
253
                    if (0 === $i) {
254
                        $method = $complete_headers['zip'];
255
                    } else {
256
                        throw new \InvalidArgumentException('Inconsistent "zip" parameter.');
257
                    }
258
                } else {
259
                    Assertion::eq($method, $complete_headers['zip'], 'Inconsistent "zip" parameter.');
260
                }
261
            } else {
262
                Assertion::notNull($method, 'Inconsistent "zip" parameter.');
263
            }
264
        }
265
266
        if (null === $method) {
267
            return;
268
        }
269
270
        $compression_method = $this->getCompressionManager()->getCompressionAlgorithm($method);
271
        Assertion::isInstanceOf($compression_method, CompressionInterface::class, sprintf('Compression method "%s" not supported.', $method));
272
273
        return $compression_method;
274
    }
275
276
    /**
277
     * @param \Jose\Object\JWEInterface $jwe
278
     *
279
     * @return \Jose\Algorithm\ContentEncryptionAlgorithmInterface
280
     */
281
    private function getContentEncryptionAlgorithm(JWEInterface $jwe)
282
    {
283
        $algorithm = null;
284
285
        foreach ($jwe->getRecipients() as $recipient) {
286
            $complete_headers = array_merge(
287
                $jwe->getSharedProtectedHeaders(),
288
                $jwe->getSharedHeaders(),
289
                $recipient->getHeaders()
290
            );
291
            Assertion::keyExists($complete_headers, 'enc', 'Parameter "enc" is missing.');
292
            if (null === $algorithm) {
293
                $algorithm = $complete_headers['enc'];
294
            } else {
295
                Assertion::eq($algorithm, $complete_headers['enc'], 'Foreign content encryption algorithms are not allowed.');
296
            }
297
        }
298
299
        $content_encryption_algorithm = $this->getJWAManager()->getAlgorithm($algorithm);
300
        Assertion::isInstanceOf($content_encryption_algorithm, ContentEncryptionAlgorithmInterface::class, sprintf('The content encryption algorithm "%s" is not supported or not a content encryption algorithm instance.', $algorithm));
301
302
        return $content_encryption_algorithm;
303
    }
304
305
    /**
306
     * @param \Jose\Object\JWEInterface                           $jwe
307
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
308
     * @param \Jose\Compression\CompressionInterface|null         $compression_method
309
     */
310
    private function encryptJWE(JWEInterface &$jwe,
311
                                ContentEncryptionAlgorithmInterface $content_encryption_algorithm,
312
                                CompressionInterface $compression_method = null
313
    ) {
314
        if (!empty($jwe->getSharedProtectedHeaders())) {
315
            $jwe = $jwe->withEncodedSharedProtectedHeaders(Base64Url::encode(json_encode($jwe->getSharedProtectedHeaders())));
316
        }
317
318
        // We encrypt the payload and get the tag
319
        $tag = null;
320
        $payload = $this->preparePayload($jwe->getPayload(), $compression_method);
321
322
        $ciphertext = $content_encryption_algorithm->encryptContent(
323
            $payload,
324
            $jwe->getContentEncryptionKey(),
325
            $jwe->getIV(),
326
            $jwe->getAAD(),
327
            $jwe->getEncodedSharedProtectedHeaders(),
328
            $tag
329
        );
330
        $jwe = $jwe->withCiphertext($ciphertext);
331
332
        // Tag
333
        if (null !== $tag) {
334
            $jwe = $jwe->withTag($tag);
335
        }
336
    }
337
338
    /**
339
     * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface     $key_encryption_algorithm
340
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
341
     * @param \Jose\Object\JWKInterface                           $recipient_key
342
     */
343
    private function checkKeys(KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, JWKInterface $recipient_key)
344
    {
345
        $this->checkKeyUsage($recipient_key, 'encryption');
346
        if ('dir' !== $key_encryption_algorithm->getAlgorithmName()) {
347
            $this->checkKeyAlgorithm($recipient_key, $key_encryption_algorithm->getAlgorithmName());
348
        } else {
349
            $this->checkKeyAlgorithm($recipient_key, $content_encryption_algorithm->getAlgorithmName());
350
        }
351
    }
352
353
    /**
354
     * @param string $current
355
     * @param string $new
356
     *
357
     * @return bool
358
     */
359
    private function areKeyManagementModesCompatible($current, $new)
360
    {
361
        $agree = KeyEncryptionAlgorithmInterface::MODE_AGREEMENT;
362
        $dir = KeyEncryptionAlgorithmInterface::MODE_DIRECT;
363
        $enc = KeyEncryptionAlgorithmInterface::MODE_ENCRYPT;
364
        $wrap = KeyEncryptionAlgorithmInterface::MODE_WRAP;
365
366
        $supported_key_management_mode_combinations = [
367
            $enc.$enc     => true,
368
            $enc.$wrap    => true,
369
            $wrap.$enc    => true,
370
            $wrap.$wrap   => true,
371
            $agree.$agree => false,
372
            $agree.$dir   => false,
373
            $agree.$enc   => false,
374
            $agree.$wrap  => false,
375
            $dir.$agree   => false,
376
            $dir.$dir     => false,
377
            $dir.$enc     => false,
378
            $dir.$wrap    => false,
379
            $enc.$agree   => false,
380
            $enc.$dir     => false,
381
            $wrap.$agree  => false,
382
            $wrap.$dir    => false,
383
        ];
384
385
        if (array_key_exists($current.$new, $supported_key_management_mode_combinations)) {
386
            return $supported_key_management_mode_combinations[$current.$new];
387
        }
388
389
        return false;
390
    }
391
392
    /**
393
     * @param string                                      $payload
394
     * @param \Jose\Compression\CompressionInterface|null $compression_method
395
     *
396
     * @return string
397
     */
398
    private function preparePayload($payload, CompressionInterface $compression_method = null)
399
    {
400
        $prepared = is_string($payload) ? $payload : json_encode($payload);
401
402
        Assertion::notNull($prepared, 'The payload is empty or cannot encoded into JSON.');
403
404
        if (null === $compression_method) {
405
            return $prepared;
406
        }
407
        $compressed_payload = $compression_method->compress($prepared);
408
        Assertion::string($compressed_payload, 'Compression failed.');
409
410
        return $compressed_payload;
411
    }
412
413
    /**
414
     * @param array                                               $complete_headers
415
     * @param string                                              $cek
416
     * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface     $key_encryption_algorithm
417
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
418
     * @param \Jose\Object\JWKInterface                           $recipient_key
419
     * @param array                                               $additional_headers
420
     *
421
     * @return string|null
422
     */
423
    private function getEncryptedKey(array $complete_headers, $cek, KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, JWKInterface $recipient_key)
424
    {
425
        if ($key_encryption_algorithm instanceof KeyEncryptionInterface) {
426
            return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers);
427
        } elseif ($key_encryption_algorithm instanceof KeyWrappingInterface) {
428
            return $this->getEncryptedKeyFromKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers);
429
        } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) {
430
            return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $content_encryption_algorithm, $additional_headers, $recipient_key);
431
        }
432
433
        // Using KeyAgreementInterface or DirectEncryptionInterface, the encrypted key is an empty string
434
    }
435
436
    /**
437
     * @param array                                                       $complete_headers
438
     * @param string                                                      $cek
439
     * @param \Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface $key_encryption_algorithm
440
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface         $content_encryption_algorithm
441
     * @param array                                                       $additional_headers
442
     * @param \Jose\Object\JWKInterface                                   $recipient_key
443
     *
444
     * @return string
445
     */
446
    private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $complete_headers, $cek, KeyAgreementWrappingInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, JWKInterface $recipient_key)
447
    {
448
        $jwt_cek = $key_encryption_algorithm->wrapAgreementKey($recipient_key, $cek, $content_encryption_algorithm->getCEKSize(), $complete_headers, $additional_headers);
449
450
        return $jwt_cek;
451
    }
452
453
    /**
454
     * @param array                                                $complete_headers
455
     * @param string                                               $cek
456
     * @param \Jose\Algorithm\KeyEncryption\KeyEncryptionInterface $key_encryption_algorithm
457
     * @param \Jose\Object\JWKInterface                            $recipient_key
458
     * @param array                                                $additional_headers
459
     *
460
     * @return string
461
     */
462
    private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $complete_headers, $cek, KeyEncryptionInterface $key_encryption_algorithm, JWKInterface $recipient_key, array &$additional_headers)
463
    {
464
        return $key_encryption_algorithm->encryptKey(
465
            $recipient_key,
466
            $cek,
467
            $complete_headers,
468
            $additional_headers
469
        );
470
    }
471
472
    /**
473
     * @param array                                              $complete_headers
474
     * @param string                                             $cek
475
     * @param \Jose\Algorithm\KeyEncryption\KeyWrappingInterface $key_encryption_algorithm
476
     * @param \Jose\Object\JWKInterface                          $recipient_key
477
     * @param array                                              $additional_headers
478
     *
479
     * @return string
480
     */
481
    private function getEncryptedKeyFromKeyWrappingAlgorithm(array $complete_headers, $cek, KeyWrappingInterface $key_encryption_algorithm, JWKInterface $recipient_key, &$additional_headers)
482
    {
483
        return $key_encryption_algorithm->wrapKey(
484
            $recipient_key,
485
            $cek,
486
            $complete_headers,
487
            $additional_headers
488
        );
489
    }
490
491
    /**
492
     * @param array $complete_headers
493
     *
494
     * @return \Jose\Algorithm\KeyEncryptionAlgorithmInterface
495
     */
496
    private function findKeyEncryptionAlgorithm(array $complete_headers)
497
    {
498
        Assertion::keyExists($complete_headers, 'alg', 'Parameter "alg" is missing.');
499
500
        $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']);
501
        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']));
502
503
        return $key_encryption_algorithm;
504
    }
505
506
    /**
507
     * @param int $size
508
     *
509
     * @return string
510
     */
511
    private function createCEK($size)
512
    {
513
        return StringUtil::generateRandomBytes($size / 8);
514
    }
515
516
    /**
517
     * @param int $size
518
     *
519
     * @return string
520
     */
521
    private function createIV($size)
522
    {
523
        return StringUtil::generateRandomBytes($size / 8);
524
    }
525
}
526