Failed Conditions
Push — master ( 0ecb40...d7fc29 )
by Florent
03:02
created

EncrypterTrait::getKeyManagementMode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 21
rs 9.3142
cc 3
eloc 13
nc 3
nop 1
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\Behaviour;
13
14
use Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Jose\Algorithm;
17
use Jose\Compression;
18
use Jose\Object;
19
20
trait EncrypterTrait
21
{
22
    /**
23
     * @param \Jose\Object\JWEInterface $jwe
24
     * @param null                      $content_encryption_algorithm
25
     * @param null                      $compression_method
26
     * @param null                      $key_management_mode
27
     * @param null                      $cek
28
     * @param array                     $additional_headers
29
     */
30
    private function prepareEncryptionProcess(Object\JWEInterface &$jwe, &$content_encryption_algorithm, &$compression_method, &$key_management_mode, &$cek, array &$additional_headers)
31
    {
32
        $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($jwe);
33
        $compression_method = $this->getCompressionMethod($jwe);
34
        $key_management_mode = $this->getKeyManagementMode($jwe);
35
        $cek = $this->determineCEK($jwe, $content_encryption_algorithm, $key_management_mode, $additional_headers);
36
    }
37
38
    /**
39
     * @param \Jose\Object\JWEInterface                           $jwe
40
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
41
     * @param string                                              $key_management_mode
42
     * @param array                                               $additional_headers
43
     *
44
     * @return string
45
     */
46
    private function determineCEK(Object\JWEInterface $jwe, Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm, $key_management_mode, array &$additional_headers)
47
    {
48
        switch ($key_management_mode) {
49
            case Algorithm\KeyEncryption\KeyEncryptionInterface::MODE_ENCRYPT:
50
            case Algorithm\KeyEncryption\KeyEncryptionInterface::MODE_WRAP:
51
                return $this->createCEK($content_encryption_algorithm->getCEKSize());
52
            case Algorithm\KeyEncryption\KeyEncryptionInterface::MODE_AGREEMENT:
53
                Assertion::eq(1, $jwe->countRecipients(), 'Unable to encrypt for multiple recipients using key agreement algorithms.');
54
                $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $jwe->getRecipient(0)->getHeaders());
55
                $algorithm = $this->findKeyEncryptionAlgorithm($complete_headers);
0 ignored issues
show
Bug introduced by
It seems like findKeyEncryptionAlgorithm() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
56
57
                return $algorithm->getAgreementKey($content_encryption_algorithm->getCEKSize(), $content_encryption_algorithm->getAlgorithmName(), $jwe->getRecipient(0)->getRecipientKey(), $complete_headers, $additional_headers);
58
            case Algorithm\KeyEncryption\KeyEncryptionInterface::MODE_DIRECT:
59
                Assertion::eq(1, $jwe->countRecipients(), 'Unable to encrypt for multiple recipients using key agreement algorithms.');
60
                Assertion::eq($jwe->getRecipient(0)->getRecipientKey()->get('kty'), 'oct', 'Wrong key type.');
61
                Assertion::true($jwe->getRecipient(0)->getRecipientKey()->has('k'), 'The key parameter "k" is missing.');
62
63
                return Base64Url::decode($jwe->getRecipient(0)->getRecipientKey()->get('k'));
64
            default:
65
                throw new \InvalidArgumentException(sprintf('Unsupported key management mode "%s".', $key_management_mode));
66
        }
67
    }
68
69
    /**
70
     * @param \Jose\Object\JWEInterface $jwe
71
     *
72
     * @return string
73
     */
74
    private function getKeyManagementMode(Object\JWEInterface $jwe)
75
    {
76
        $mode = null;
77
        $recipients = $jwe->getRecipients();
78
79
        foreach ($recipients as $recipient) {
80
            $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $recipient->getHeaders());
81
            Assertion::keyExists($complete_headers, 'alg', 'Parameter "alg" is missing.');
82
83
            $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']);
0 ignored issues
show
Bug introduced by
It seems like getJWAManager() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
84
            Assertion::isInstanceOf($key_encryption_algorithm, Algorithm\KeyEncryptionAlgorithmInterface::class, sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $complete_headers['alg']));
85
86
            if (null === $mode) {
87
                $mode = $key_encryption_algorithm->getKeyManagementMode();
88
            } else {
89
                Assertion::true($this->areKeyManagementModesCompatible($mode, $key_encryption_algorithm->getKeyManagementMode()), 'Foreign key management mode forbidden.');
90
            }
91
        }
92
93
        return $mode;
94
    }
95
96
    /**
97
     * @param \Jose\Object\JWEInterface $jwe
98
     *
99
     * @return \Jose\Compression\CompressionInterface|null
100
     */
101
    private function getCompressionMethod(Object\JWEInterface $jwe)
102
    {
103
        $method = null;
104
        $nb_recipients = $jwe->countRecipients();
105
106
        for ($i = 0; $i < $nb_recipients; $i++) {
107
            $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $jwe->getRecipient($i)->getHeaders());
108
            if (array_key_exists('zip', $complete_headers)) {
109
                if (null === $method) {
110
                    if (0 === $i) {
111
                        $method = $complete_headers['zip'];
112
                    } else {
113
                        throw new \InvalidArgumentException('Inconsistent "zip" parameter.');
114
                    }
115
                } else {
116
                    Assertion::eq($method, $complete_headers['zip'], 'Inconsistent "zip" parameter.');
117
                }
118
            } else {
119
                Assertion::eq(null, $method, 'Inconsistent "zip" parameter.');
120
            }
121
        }
122
123
        if (null === $method) {
124
            return;
125
        }
126
127
        $compression_method = $this->getCompressionManager()->getCompressionAlgorithm($method);
0 ignored issues
show
Bug introduced by
It seems like getCompressionManager() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
128
        Assertion::isInstanceOf($compression_method, Compression\CompressionInterface::class, sprintf('Compression method "%s" not supported.', $method));
129
130
        return $compression_method;
131
    }
132
133
    /**
134
     * @param \Jose\Object\JWEInterface $jwe
135
     *
136
     * @return \Jose\Algorithm\ContentEncryptionAlgorithmInterface
137
     */
138
    private function getContentEncryptionAlgorithm(Object\JWEInterface $jwe)
139
    {
140
        $algorithm = null;
141
142
        foreach ($jwe->getRecipients() as $recipient) {
143
            $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $recipient->getHeaders());
144
            Assertion::keyExists($complete_headers, 'enc', 'Parameter "enc" is missing.');
145
            if (null === $algorithm) {
146
                $algorithm = $complete_headers['enc'];
147
            } else {
148
                Assertion::eq($algorithm, $complete_headers['enc'], 'Foreign content encryption algorithms are not allowed.');
149
            }
150
        }
151
152
        $content_encryption_algorithm = $this->getJWAManager()->getAlgorithm($algorithm);
0 ignored issues
show
Bug introduced by
It seems like getJWAManager() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
153
        Assertion::isInstanceOf($content_encryption_algorithm, Algorithm\ContentEncryptionAlgorithmInterface::class, sprintf('The content encryption algorithm "%s" is not supported or not a content encryption algorithm instance.', $algorithm));
154
155
        return $content_encryption_algorithm;
156
    }
157
158
    /**
159
     * @param string $current
160
     * @param string $new
161
     *
162
     * @return bool
163
     */
164
    private function areKeyManagementModesCompatible($current, $new)
165
    {
166
        $agree = Algorithm\KeyEncryptionAlgorithmInterface::MODE_AGREEMENT;
167
        $dir = Algorithm\KeyEncryptionAlgorithmInterface::MODE_DIRECT;
168
        $enc = Algorithm\KeyEncryptionAlgorithmInterface::MODE_ENCRYPT;
169
        $wrap = Algorithm\KeyEncryptionAlgorithmInterface::MODE_WRAP;
170
        $supported_key_management_mode_combinations = [$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,];
171
172
        if (array_key_exists($current.$new, $supported_key_management_mode_combinations)) {
173
            return $supported_key_management_mode_combinations[$current.$new];
174
        }
175
176
        return false;
177
    }
178
179
    /**
180
     * @param int $size
181
     *
182
     * @return string
183
     */
184
    private function createCEK($size)
185
    {
186
        return random_bytes($size / 8);
187
    }
188
}
189