Completed
Push — master ( 7ac082...fc335c )
by Florent
02:51
created

Loader   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 269
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 18
Bugs 3 Features 2
Metric Value
wmc 36
c 18
b 3
f 2
lcom 1
cbo 7
dl 0
loc 269
rs 8.8

15 Methods

Rating   Name   Duplication   Size   Complexity  
A loadAndDecryptUsingKey() 0 7 1
A loadAndDecryptUsingKeySet() 0 4 1
A loadAndVerifySignatureUsingKey() 0 7 1
A loadAndVerifySignatureUsingKeySet() 0 4 1
A loadAndVerifySignatureUsingKeyAndDetachedPayload() 0 7 1
A loadAndVerifySignatureUsingKeySetAndDetachedPayload() 0 4 1
A loadAndDecrypt() 0 10 1
A loadAndVerifySignature() 0 10 1
A load() 0 13 3
B convert() 0 20 7
B fromFlattenedSerializationRecipientToSerialization() 0 22 5
A fromFlattenedSerializationSignatureToSerialization() 0 21 4
A fromCompactSerializationToSerialization() 0 16 3
A fromCompactSerializationRecipientToSerialization() 0 20 4
A fromCompactSerializationSignatureToSerialization() 0 16 2
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 Jose\Behaviour\HasLogger;
16
use Jose\Object\JWKInterface;
17
use Jose\Object\JWKSet;
18
use Jose\Object\JWKSetInterface;
19
use Jose\Object\JWSInterface;
20
use Jose\Object\JWEInterface;
21
use Jose\Util\JWELoader;
22
use Jose\Util\JWSLoader;
23
use Psr\Log\LoggerInterface;
24
use Psr\Log\LogLevel;
25
26
/**
27
 * Class able to load JWS or JWE.
28
 * JWS object can also be verified.
29
 */
30
final class Loader implements LoaderInterface
31
{
32
    use HasLogger;
33
34
    /**
35
     * {@inheritdoc}
36
     */
37
    public function loadAndDecryptUsingKey($input, JWKInterface $jwk, array $allowed_key_encryption_algorithms, array $allowed_content_encryption_algorithms, &$recipient_index = null, LoggerInterface $logger = null)
38
    {
39
        $jwk_set = new JWKSet();
40
        $jwk_set->addKey($jwk);
41
42
        return $this->loadAndDecrypt($input, $jwk_set, $allowed_key_encryption_algorithms, $allowed_content_encryption_algorithms, $recipient_index, $logger);
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function loadAndDecryptUsingKeySet($input, JWKSetInterface $jwk_set, array $allowed_key_encryption_algorithms, array $allowed_content_encryption_algorithms, &$recipient_index = null, LoggerInterface $logger = null)
49
    {
50
        return $this->loadAndDecrypt($input, $jwk_set, $allowed_key_encryption_algorithms, $allowed_content_encryption_algorithms, $recipient_index, $logger);
51
    }
52
    
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function loadAndVerifySignatureUsingKey($input, JWKInterface $jwk, array $allowed_algorithms, &$signature_index = null, LoggerInterface $logger = null)
57
    {
58
        $jwk_set = new JWKSet();
59
        $jwk_set->addKey($jwk);
60
61
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, null, $signature_index, $logger);
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function loadAndVerifySignatureUsingKeySet($input, JWKSetInterface $jwk_set, array $allowed_algorithms, &$signature_index = null, LoggerInterface $logger = null)
68
    {
69
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, null, $signature_index, $logger);
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function loadAndVerifySignatureUsingKeyAndDetachedPayload($input, JWKInterface $jwk, array $allowed_algorithms, $detached_payload, &$signature_index = null, LoggerInterface $logger = null)
76
    {
77
        $jwk_set = new JWKSet();
78
        $jwk_set->addKey($jwk);
79
80
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, $detached_payload, $signature_index, $logger);
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public function loadAndVerifySignatureUsingKeySetAndDetachedPayload($input, JWKSetInterface $jwk_set, array $allowed_algorithms, $detached_payload, &$signature_index = null, LoggerInterface $logger = null)
87
    {
88
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, $detached_payload, $signature_index, $logger);
89
    }
90
91
    /**
92
     * @param string                        $input
93
     * @param \Jose\Object\JWKSetInterface  $jwk_set
94
     * @param array                         $allowed_key_encryption_algorithms
95
     * @param array                         $allowed_content_encryption_algorithms
96
     * @param \Psr\Log\LoggerInterface|null $logger
97
     *
98
     * @return \Jose\Object\JWEInterface
99
     */
100
    private function loadAndDecrypt($input, JWKSetInterface $jwk_set, array $allowed_key_encryption_algorithms, array $allowed_content_encryption_algorithms, &$recipient_index = null, LoggerInterface $logger = null)
101
    {
102
        $jwt = $this->load($input);
103
        Assertion::isInstanceOf($jwt, JWEInterface::class, 'The input is not a valid JWE');
104
        $decrypted = Decrypter::createDecrypter($allowed_key_encryption_algorithms, $allowed_content_encryption_algorithms, ['DEF', 'ZLIB', 'GZ'], $logger);
105
106
        $decrypted->decryptUsingKeySet($jwt, $jwk_set, $recipient_index);
0 ignored issues
show
Bug introduced by
It seems like $jwt defined by $this->load($input) on line 102 can also be of type object<Jose\Object\JWSInterface>; however, Jose\Decrypter::decryptUsingKeySet() does only seem to accept object<Jose\Object\JWEInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
107
108
        return $jwt;
109
    }
110
111
    /**
112
     * @param string                        $input
113
     * @param \Jose\Object\JWKSetInterface  $jwk_set
114
     * @param array                         $allowed_algorithms
115
     * @param string|null                   $detached_payload
116
     * @param \Psr\Log\LoggerInterface|null $logger
117
     *
118
     * @return \Jose\Object\JWSInterface
119
     */
120
    private function loadAndVerifySignature($input, JWKSetInterface $jwk_set, array $allowed_algorithms, $detached_payload = null, &$signature_index = null, LoggerInterface $logger = null)
121
    {
122
        $jwt = $this->load($input);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->load($input); of type Jose\Object\JWSInterface|Jose\Object\JWEInterface adds the type Jose\Object\JWEInterface to the return on line 128 which is incompatible with the return type documented by Jose\Loader::loadAndVerifySignature of type Jose\Object\JWSInterface.
Loading history...
123
        Assertion::isInstanceOf($jwt, JWSInterface::class, 'The input is not a valid JWS.');
124
        $verifier = Verifier::createVerifier($allowed_algorithms, $logger);
125
126
        $verifier->verifyWithKeySet($jwt, $jwk_set, $detached_payload, $signature_index);
0 ignored issues
show
Bug introduced by
It seems like $jwt defined by $this->load($input) on line 122 can also be of type object<Jose\Object\JWEInterface>; however, Jose\Verifier::verifyWithKeySet() does only seem to accept object<Jose\Object\JWSInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
127
128
        return $jwt;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function load($input)
135
    {
136
        $this->log(LogLevel::INFO, 'Trying to load the input.', ['input' => $input]);
137
        $json = $this->convert($input);
138
        if (array_key_exists('signatures', $json)) {
139
            $this->log(LogLevel::DEBUG, 'The input is a JWS.', ['json' => $json]);
140
            return JWSLoader::loadSerializedJsonJWS($json);
141
        }
142
        if (array_key_exists('recipients', $json)) {
143
            $this->log(LogLevel::DEBUG, 'The input is a JWE.', ['json' => $json]);
144
            return JWELoader::loadSerializedJsonJWE($json);
145
        }
146
    }
147
148
    /**
149
     * @param string $input
150
     *
151
     * @return array
152
     */
153
    private function convert($input)
154
    {
155
        if (is_array($data = json_decode($input, true))) {
156
            if (array_key_exists('signatures', $data) || array_key_exists('recipients', $data)) {
157
                $this->log(LogLevel::DEBUG, 'The input seems to be a JWS or a JWE.');
158
                return $data;
159
            } elseif (array_key_exists('signature', $data)) {
160
                $this->log(LogLevel::DEBUG, 'The input seems to be a flattened JWS.');
161
                return $this->fromFlattenedSerializationSignatureToSerialization($data);
162
            } elseif (array_key_exists('ciphertext', $data)) {
163
                $this->log(LogLevel::DEBUG, 'The input seems to be a flattened JWE.');
164
                return $this->fromFlattenedSerializationRecipientToSerialization($data);
165
            }
166
        } elseif (is_string($input)) {
167
            $this->log(LogLevel::DEBUG, 'The input may be a compact JWS or JWE.');
168
            return $this->fromCompactSerializationToSerialization($input);
169
        }
170
        $this->log(LogLevel::ERROR, 'The input is not supported');
171
        throw new \InvalidArgumentException('Unsupported input');
172
    }
173
174
    /**
175
     * @param $input
176
     *
177
     * @return array
178
     */
179
    private function fromFlattenedSerializationRecipientToSerialization($input)
180
    {
181
        $recipient = [];
182
        foreach (['header', 'encrypted_key'] as $key) {
183
            if (array_key_exists($key, $input)) {
184
                $recipient[$key] = $input[$key];
185
            }
186
        }
187
        $recipients = [
188
            'ciphertext' => $input['ciphertext'],
189
            'recipients' => [$recipient],
190
        ];
191
        foreach (['protected', 'unprotected', 'iv', 'aad', 'tag'] as $key) {
192
            if (array_key_exists($key, $input)) {
193
                $recipients[$key] = $input[$key];
194
            }
195
        }
196
197
        $this->log(LogLevel::INFO, 'JWE in Flattened JSON Serialization mode loaded.');
198
        
199
        return $recipients;
200
    }
201
202
    /**
203
     * @param $input
204
     *
205
     * @return array
206
     */
207
    private function fromFlattenedSerializationSignatureToSerialization($input)
208
    {
209
        $signature = [
210
            'signature' => $input['signature'],
211
        ];
212
        foreach (['protected', 'header'] as $key) {
213
            if (array_key_exists($key, $input)) {
214
                $signature[$key] = $input[$key];
215
            }
216
        }
217
218
        $temp = [];
219
        if (!empty($input['payload'])) {
220
            $temp['payload'] = $input['payload'];
221
        }
222
        $temp['signatures'] = [$signature];
223
224
        $this->log(LogLevel::INFO, 'JWS in Flattened JSON Serialization mode loaded.');
225
        
226
        return $temp;
227
    }
228
229
    /**
230
     * @param string $input
231
     *
232
     * @return array
233
     */
234
    private function fromCompactSerializationToSerialization($input)
235
    {
236
        $parts = explode('.', $input);
237
        switch (count($parts)) {
238
            case 3:
239
                $this->log(LogLevel::DEBUG, 'The input seems to be a compact JWS.');
240
                
241
                return $this->fromCompactSerializationSignatureToSerialization($parts);
242
            case 5:
243
                $this->log(LogLevel::DEBUG, 'The input seems to be a compact JWE.');
244
                
245
                return $this->fromCompactSerializationRecipientToSerialization($parts);
246
            default:
247
                throw new \InvalidArgumentException('Unsupported input');
248
        }
249
    }
250
251
    /**
252
     * @param array $parts
253
     *
254
     * @return array
255
     */
256
    private function fromCompactSerializationRecipientToSerialization(array $parts)
257
    {
258
        $recipient = [];
259
        if (!empty($parts[1])) {
260
            $recipient['encrypted_key'] = $parts[1];
261
        }
262
263
        $recipients = [
264
            'recipients' => [$recipient],
265
        ];
266
        foreach ([0 => 'protected', 2 => 'iv', 3 => 'ciphertext', 4 => 'tag'] as $part => $key) {
267
            if (!empty($parts[$part])) {
268
                $recipients[$key] = $parts[$part];
269
            }
270
        }
271
        
272
        $this->log(LogLevel::INFO, 'JWE in Compact JSON Serialization mode loaded.');
273
274
        return $recipients;
275
    }
276
277
    /**
278
     * @param array $parts
279
     *
280
     * @return array
281
     */
282
    private function fromCompactSerializationSignatureToSerialization(array $parts)
283
    {
284
        $temp = [];
285
286
        if (!empty($parts[1])) {
287
            $temp['payload'] = $parts[1];
288
        }
289
        $temp['signatures'] = [[
290
            'protected' => $parts[0],
291
            'signature' => $parts[2],
292
        ]];
293
294
        $this->log(LogLevel::INFO, 'JWS in Compact JSON Serialization mode loaded.');
295
        
296
        return $temp;
297
    }
298
}
299