Completed
Push — master ( 547bd7...8b2a0a )
by Florent
02:35
created

fromCompactSerializationRecipientToSerialization()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
rs 9.2
cc 4
eloc 10
nc 6
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;
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
25
/**
26
 * Class able to load JWS or JWE.
27
 * JWS object can also be verified.
28
 */
29
final class Loader implements LoaderInterface
30
{
31
    use HasLogger;
32
33
    public function __construct(LoggerInterface $logger = null)
34
    {
35
        if (null !== $logger) {
36
            $this->setLogger($logger);
37
        }
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function loadAndDecryptUsingKey($input, JWKInterface $jwk, array $allowed_key_encryption_algorithms, array $allowed_content_encryption_algorithms, LoggerInterface $logger = null)
44
    {
45
        $jwk_set = new JWKSet();
46
        $jwk_set->addKey($jwk);
47
48
        return $this->loadAndDecrypt($input, $jwk_set, $allowed_key_encryption_algorithms, $allowed_content_encryption_algorithms, $logger);
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function loadAndDecryptUsingKeySet($input, JWKSetInterface $jwk_set, array $allowed_key_encryption_algorithms, array $allowed_content_encryption_algorithms, LoggerInterface $logger = null)
55
    {
56
        return $this->loadAndDecrypt($input, $jwk_set, $allowed_key_encryption_algorithms, $allowed_content_encryption_algorithms, $logger);
57
    }
58
    
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function loadAndVerifySignatureUsingKey($input, JWKInterface $jwk, array $allowed_algorithms, LoggerInterface $logger = null)
63
    {
64
        $jwk_set = new JWKSet();
65
        $jwk_set->addKey($jwk);
66
67
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, null, $logger);
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    public function loadAndVerifySignatureUsingKeySet($input, JWKSetInterface $jwk_set, array $allowed_algorithms, LoggerInterface $logger = null)
74
    {
75
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, null, $logger);
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function loadAndVerifySignatureUsingKeyAndDetachedPayload($input, JWKInterface $jwk, array $allowed_algorithms, $detached_payload, LoggerInterface $logger = null)
82
    {
83
        $jwk_set = new JWKSet();
84
        $jwk_set->addKey($jwk);
85
86
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, $detached_payload, $logger);
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function loadAndVerifySignatureUsingKeySetAndDetachedPayload($input, JWKSetInterface $jwk_set, array $allowed_algorithms, $detached_payload, LoggerInterface $logger = null)
93
    {
94
        return $this->loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, $detached_payload, $logger);
95
    }
96
97
    /**
98
     * @param string                        $input
99
     * @param \Jose\Object\JWKSetInterface  $jwk_set
100
     * @param array                         $allowed_key_encryption_algorithms
101
     * @param array                         $allowed_content_encryption_algorithms
102
     * @param \Psr\Log\LoggerInterface|null $logger
103
     *
104
     * @return \Jose\Object\JWEInterface
105
     */
106
    private function loadAndDecrypt($input, JWKSetInterface $jwk_set, array $allowed_key_encryption_algorithms, array $allowed_content_encryption_algorithms, LoggerInterface $logger = null)
107
    {
108
        $jwt = $this->load($input);
109
        Assertion::isInstanceOf($jwt, JWEInterface::class, 'The input is not a valid JWE');
110
        $decrypted = Decrypter::createDecrypter($allowed_key_encryption_algorithms, $allowed_content_encryption_algorithms, ['DEF', 'ZLIB', 'GZ'], $logger);
111
112
        $decrypted->decryptUsingKeySet($jwt, $jwk_set);
0 ignored issues
show
Bug introduced by
It seems like $jwt defined by $this->load($input) on line 108 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...
113
114
        return $jwt;
115
    }
116
117
    /**
118
     * @param string                        $input
119
     * @param \Jose\Object\JWKSetInterface  $jwk_set
120
     * @param array                         $allowed_algorithms
121
     * @param string|null                   $detached_payload
122
     * @param \Psr\Log\LoggerInterface|null $logger
123
     *
124
     * @return \Jose\Object\JWSInterface
125
     */
126
    private function loadAndVerifySignature($input, JWKSetInterface $jwk_set, array $allowed_algorithms, $detached_payload = null, LoggerInterface $logger = null)
127
    {
128
        $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 134 which is incompatible with the return type documented by Jose\Loader::loadAndVerifySignature of type Jose\Object\JWSInterface.
Loading history...
129
        Assertion::isInstanceOf($jwt, JWSInterface::class, 'The input is not a valid JWS');
130
        $verifier = Verifier::createVerifier($allowed_algorithms, $logger);
131
132
        $verifier->verifyWithKeySet($jwt, $jwk_set, $detached_payload);
0 ignored issues
show
Bug introduced by
It seems like $jwt defined by $this->load($input) on line 128 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...
133
134
        return $jwt;
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function load($input)
141
    {
142
        $json = $this->convert($input);
143
        if (array_key_exists('signatures', $json)) {
144
            return JWSLoader::loadSerializedJsonJWS($json);
145
        }
146
        if (array_key_exists('recipients', $json)) {
147
            return JWELoader::loadSerializedJsonJWE($json);
148
        }
149
    }
150
151
    /**
152
     * @param string $input
153
     *
154
     * @return array
155
     */
156
    private function convert($input)
157
    {
158
        if (is_array($data = json_decode($input, true))) {
159
            if (array_key_exists('signatures', $data) || array_key_exists('recipients', $data)) {
160
                return $data;
161
            } elseif (array_key_exists('signature', $data)) {
162
                return $this->fromFlattenedSerializationSignatureToSerialization($data);
163
            } elseif (array_key_exists('ciphertext', $data)) {
164
                return $this->fromFlattenedSerializationRecipientToSerialization($data);
165
            }
166
        } elseif (is_string($input)) {
167
            return $this->fromCompactSerializationToSerialization($input);
168
        }
169
        throw new \InvalidArgumentException('Unsupported input');
170
    }
171
172
    /**
173
     * @param $input
174
     *
175
     * @return array
176
     */
177
    private function fromFlattenedSerializationRecipientToSerialization($input)
178
    {
179
        $recipient = [];
180
        foreach (['header', 'encrypted_key'] as $key) {
181
            if (array_key_exists($key, $input)) {
182
                $recipient[$key] = $input[$key];
183
            }
184
        }
185
        $recipients = [
186
            'ciphertext' => $input['ciphertext'],
187
            'recipients' => [$recipient],
188
        ];
189
        foreach (['ciphertext', 'protected', 'unprotected', 'iv', 'aad', 'tag'] as $key) {
190
            if (array_key_exists($key, $input)) {
191
                $recipients[$key] = $input[$key];
192
            }
193
        }
194
195
        return $recipients;
196
    }
197
198
    /**
199
     * @param $input
200
     *
201
     * @return array
202
     */
203
    private function fromFlattenedSerializationSignatureToSerialization($input)
204
    {
205
        $signature = [
206
            'signature' => $input['signature'],
207
        ];
208
        foreach (['protected', 'header'] as $key) {
209
            if (array_key_exists($key, $input)) {
210
                $signature[$key] = $input[$key];
211
            }
212
        }
213
214
        $temp = [];
215
        if (!empty($input['payload'])) {
216
            $temp['payload'] = $input['payload'];
217
        }
218
        $temp['signatures'] = [$signature];
219
220
        return $temp;
221
    }
222
223
    /**
224
     * @param string $input
225
     *
226
     * @return array
227
     */
228
    private function fromCompactSerializationToSerialization($input)
229
    {
230
        $parts = explode('.', $input);
231
        switch (count($parts)) {
232
            case 3:
233
                return $this->fromCompactSerializationSignatureToSerialization($parts);
234
            case 5:
235
                return $this->fromCompactSerializationRecipientToSerialization($parts);
236
            default:
237
                throw new \InvalidArgumentException('Unsupported input');
238
        }
239
    }
240
241
    /**
242
     * @param array $parts
243
     *
244
     * @return array
245
     */
246
    private function fromCompactSerializationRecipientToSerialization(array $parts)
247
    {
248
        $recipient = [];
249
        if (!empty($parts[1])) {
250
            $recipient['encrypted_key'] = $parts[1];
251
        }
252
253
        $recipients = [
254
            'recipients' => [$recipient],
255
        ];
256
        foreach ([3 => 'ciphertext', 0 => 'protected', 2 => 'iv', 4 => 'tag'] as $part => $key) {
257
            if (!empty($parts[$part])) {
258
                $recipients[$key] = $parts[$part];
259
            }
260
        }
261
262
        return $recipients;
263
    }
264
265
    /**
266
     * @param array $parts
267
     *
268
     * @return array
269
     */
270
    private function fromCompactSerializationSignatureToSerialization(array $parts)
271
    {
272
        $temp = [];
273
274
        if (!empty($parts[1])) {
275
            $temp['payload'] = $parts[1];
276
        }
277
        $temp['signatures'] = [[
278
            'protected' => $parts[0],
279
            'signature' => $parts[2],
280
        ]];
281
282
        return $temp;
283
    }
284
}
285