Completed
Push — v2.0.x ( 7a58b6 )
by Florent
24:58
created

Decrypter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 7
nc 1
nop 3
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2015 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 Jose\Algorithm\ContentEncryptionAlgorithmInterface;
15
use Jose\Algorithm\JWAInterface;
16
use Jose\Algorithm\JWAManagerInterface;
17
use Jose\Algorithm\KeyEncryption\DirectEncryptionInterface;
18
use Jose\Algorithm\KeyEncryption\KeyAgreementInterface;
19
use Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface;
20
use Jose\Algorithm\KeyEncryption\KeyEncryptionInterface;
21
use Jose\Behaviour\HasCheckerManager;
22
use Jose\Behaviour\HasCompressionManager;
23
use Jose\Behaviour\HasJWAManager;
24
use Jose\Behaviour\HasKeyChecker;
25
use Jose\Behaviour\HasPayloadConverter;
26
use Jose\Checker\CheckerManagerInterface;
27
use Jose\Compression\CompressionManagerInterface;
28
use Jose\Object\JWE;
29
use Jose\Object\JWEInterface;
30
use Jose\Object\JWKInterface;
31
use Jose\Object\JWKSet;
32
use Jose\Object\JWKSetInterface;
33
34
/**
35
 */
36
final class Decrypter implements DecrypterInterface
37
{
38
    use HasKeyChecker;
39
    use HasJWAManager;
40
    use HasCheckerManager;
41
    use HasPayloadConverter;
42
    use HasCompressionManager;
43
44
    /**
45
     * Loader constructor.
46
     *
47
     * @param \Jose\Algorithm\JWAManagerInterface            $jwa_manager
48
     * @param \Jose\Compression\CompressionManagerInterface  $compression_manager
49
     * @param \Jose\Checker\CheckerManagerInterface          $checker_manager
50
     */
51
    public function __construct(
52
        JWAManagerInterface $jwa_manager,
53
        CompressionManagerInterface $compression_manager,
54
        CheckerManagerInterface $checker_manager)
55
    {
56
        $this->setJWAManager($jwa_manager);
57
        $this->setCompressionManager($compression_manager);
58
        $this->setCheckerManager($checker_manager);
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function decryptUsingKey(JWEInterface &$jwe, JWKInterface $jwk)
65
    {
66
        $jwk_set = new JWKSet();
67
        $jwk_set = $jwk_set->addKey($jwk);
68
69
        return $this->decryptUsingKeySet($jwe, $jwk_set);
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function decryptUsingKeySet(JWEInterface &$jwe, JWKSetInterface $jwk_set)
76
    {
77
        $this->checkCompleteHeader($jwe);
78
        $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($jwe->getHeader('alg'));
0 ignored issues
show
Bug introduced by
The method getHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
79
        $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($jwe->getHeader('enc'));
0 ignored issues
show
Bug introduced by
The method getHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
80
        $decrypted = false;
81
        foreach ($jwk_set as $jwk) {
82
            if (!$this->checkKeyUsage($jwk, 'decryption')) {
83
                continue;
84
            }
85
            if (!$this->checkKeyAlgorithm($jwk, $key_encryption_algorithm->getAlgorithmName())) {
86
                continue;
87
            }
88
            try {
89
                $cek = $this->decryptCEK($key_encryption_algorithm, $content_encryption_algorithm, $jwk, $jwe->getEncryptedKey(), $jwe->getHeaders());
0 ignored issues
show
Bug introduced by
The method getEncryptedKey() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
Bug introduced by
The method getHeaders() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
90
91
                if (null !== $cek) {
92
                    $decrypted = $this->decryptPayload($jwe, $cek, $content_encryption_algorithm);
93
                }
94
            } catch (\Exception $e) {
95
                //We do nothing, we continue with other keys
96
                continue;
97
            }
98
            if (true === $decrypted) {
99
                $this->getCheckerManager()->checkJWT($jwe);
100
101
                return true;
102
            }
103
        }
104
105
        return false;
106
    }
107
108
    /**
109
     * @param \Jose\Algorithm\JWAInterface                        $key_encryption_algorithm
110
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
111
     * @param \Jose\Object\JWKInterface                           $key
112
     * @param string|null                                         $encrypted_cek
113
     * @param array                                               $header
114
     *
115
     * @return string|null
116
     */
117
    private function decryptCEK(JWAInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, JWKInterface $key, $encrypted_cek, array $header)
118
    {
119
        if ($key_encryption_algorithm instanceof DirectEncryptionInterface) {
120
            return $key_encryption_algorithm->getCEK($key);
121
        } elseif ($key_encryption_algorithm instanceof KeyAgreementInterface) {
122
            return $key_encryption_algorithm->getAgreementKey($content_encryption_algorithm->getCEKSize(), $key, null, $header);
123
        } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) {
124
            return $key_encryption_algorithm->unwrapAgreementKey($key, $encrypted_cek, $content_encryption_algorithm->getCEKSize(), $header);
125
        } elseif ($key_encryption_algorithm instanceof KeyEncryptionInterface) {
126
            return $key_encryption_algorithm->decryptKey($key, $encrypted_cek, $header);
127
        } else {
128
            throw new \RuntimeException('Unsupported CEK generation');
129
        }
130
    }
131
132
    /**
133
     * @param \Jose\Object\JWEInterface                           $jwe
134
     * @param string                                              $cek
135
     * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm
136
     *
137
     * @return bool
138
     */
139
    private function decryptPayload(JWEInterface &$jwe, $cek, ContentEncryptionAlgorithmInterface $content_encryption_algorithm)
140
    {
141
        $payload = $content_encryption_algorithm->decryptContent(
142
            $jwe->getCiphertext(),
143
            $cek,
144
            $jwe->getIV(),
145
            $jwe->getAAD(),
146
            $jwe->getEncodedProtectedHeader(),
0 ignored issues
show
Bug introduced by
The method getEncodedProtectedHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
147
            $jwe->getTag()
148
        );
149
150
        if (null === $payload) {
151
            return false;
152
        }
153
154
        if ($jwe->hasHeader('zip')) {
0 ignored issues
show
Bug introduced by
The method hasHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
155
            $compression_method = $this->getCompressionMethod($jwe->getHeader('zip'));
0 ignored issues
show
Bug introduced by
The method getHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
156
            $payload = $compression_method->uncompress($payload);
157
            if (!is_string($payload)) {
158
                throw new \RuntimeException('Decompression failed');
159
            }
160
        }
161
162
        $result = new JWE(
163
            $jwe->getInput(),
0 ignored issues
show
Bug introduced by
The method getInput() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
Unused Code introduced by
The call to JWE::__construct() has too many arguments starting with $jwe->getInput().

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...
164
            $jwe->getCiphertext(),
165
            $jwe->getEncryptedKey(),
0 ignored issues
show
Bug introduced by
The method getEncryptedKey() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
166
            $jwe->getIV(),
167
            $jwe->getAAD(),
168
            $jwe->getTag(),
169
            $jwe->getEncodedProtectedHeader(),
0 ignored issues
show
Bug introduced by
The method getEncodedProtectedHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
170
            $jwe->getUnprotectedHeaders(),
0 ignored issues
show
Bug introduced by
The method getUnprotectedHeaders() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
171
            $payload
172
        );
173
174
        $jwe = $result;
175
176
        return true;
177
    }
178
179
    /**
180
     * @param \Jose\Object\JWEInterface $jwe
181
     *
182
     * @throws \InvalidArgumentException
183
     */
184
    private function checkCompleteHeader($jwe)
185
    {
186
        foreach (['enc', 'alg'] as $key) {
187
            if (!$jwe->hasHeader($key)) {
0 ignored issues
show
Bug introduced by
The method hasHeader() does not seem to exist on object<Jose\Object\JWEInterface>.

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...
188
                throw new \InvalidArgumentException(sprintf("Parameters '%s' is missing.", $key));
189
            }
190
        }
191
    }
192
193
    /**
194
     * @param string $algorithm
195
     *
196
     * @return \Jose\Algorithm\KeyEncryption\DirectEncryptionInterface|\Jose\Algorithm\KeyEncryption\KeyEncryptionInterface|\Jose\Algorithm\KeyEncryption\KeyAgreementInterface|\Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface
197
     */
198
    private function getKeyEncryptionAlgorithm($algorithm)
199
    {
200
        $key_encryption_algorithm = $this->getJWAManager()->getAlgorithm($algorithm);
201
        foreach ([
202
                     '\Jose\Algorithm\KeyEncryption\DirectEncryptionInterface',
203
                     '\Jose\Algorithm\KeyEncryption\KeyEncryptionInterface',
204
                     '\Jose\Algorithm\KeyEncryption\KeyAgreementInterface',
205
                     '\Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface',
206
                 ] as $class) {
207
            if ($key_encryption_algorithm instanceof $class) {
208
                return $key_encryption_algorithm;
209
            }
210
        }
211
        throw new \RuntimeException(sprintf("The key encryption algorithm '%s' is not supported or not a key encryption algorithm instance.", $algorithm));
212
    }
213
214
    /**
215
     * @param $algorithm
216
     *
217
     * @return \Jose\Algorithm\ContentEncryptionAlgorithmInterface
218
     */
219
    private function getContentEncryptionAlgorithm($algorithm)
220
    {
221
        $content_encryption_algorithm = $this->getJWAManager()->getAlgorithm($algorithm);
222
        if (!$content_encryption_algorithm instanceof ContentEncryptionAlgorithmInterface) {
223
            throw new \RuntimeException("The algorithm '".$algorithm."' does not implement ContentEncryptionInterface.");
224
        }
225
226
        return $content_encryption_algorithm;
227
    }
228
229
    /**
230
     * @param string $method
231
     *
232
     * @throws \InvalidArgumentException
233
     *
234
     * @return \Jose\Compression\CompressionInterface
235
     */
236
    private function getCompressionMethod($method)
237
    {
238
        $compression_method = $this->getCompressionManager()->getCompressionAlgorithm($method);
239
        if (null === $compression_method) {
240
            throw new \InvalidArgumentException(sprintf("Compression method '%s' not supported"), $method);
241
        }
242
243
        return $compression_method;
244
    }
245
}
246