Completed
Push — develop ( 686594...b5844e )
by Florent
03:11
created

Loader::loadAndVerifySignatureUsingKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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