Completed
Push — master ( 933d17...6b3c88 )
by Florent
02:40
created

Loader::loadAndVerifySignatureUsingKeySet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
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\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
    /**
32
     * {@inheritdoc}
33
     */
34
    public static function loadAndVerifySignatureUsingKey($input, JWKInterface $jwk, array $allowed_algorithms, LoggerInterface $logger = null)
35
    {
36
        $jwk_set = new JWKSet();
37
        $jwk_set = $jwk_set->addKey($jwk);
38
39
        return self::loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, null, $logger);
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public static function loadAndVerifySignatureUsingKeySet($input, JWKSetInterface $jwk_set, array $allowed_algorithms, LoggerInterface $logger = null)
46
    {
47
        return self::loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, null, $logger);
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public static function loadAndVerifySignatureUsingKeyAndDetachedPayload($input, JWKInterface $jwk, array $allowed_algorithms, $detached_payload, LoggerInterface $logger = null)
54
    {
55
        $jwk_set = new JWKSet();
56
        $jwk_set = $jwk_set->addKey($jwk);
57
58
        return self::loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, $detached_payload, $logger);
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public static function loadAndVerifySignatureUsingKeySetAndDetachedPayload($input, JWKSetInterface $jwk_set, array $allowed_algorithms, $detached_payload, LoggerInterface $logger = null)
65
    {
66
        return self::loadAndVerifySignature($input, $jwk_set, $allowed_algorithms, $detached_payload, $logger);
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    private static function loadAndVerifySignature($input, JWKSetInterface $jwk_set, array $allowed_algorithms, $detached_payload = null, LoggerInterface $logger = null)
73
    {
74
        $jwt = self::load($input);
75
        Assertion::isInstanceOf($jwt, JWSInterface::class, 'The input is not a valid JWS');
76
        $verifier = VerifierFactory::createVerifier($allowed_algorithms, $logger);
77
78
        $result = $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 74 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...
79
        Assertion::integer($result, 'Unable to verify or decrypt the input');
80
81
        return $jwt;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public static function load($input)
88
    {
89
        $json = self::convert($input);
90
        if (array_key_exists('signatures', $json)) {
91
            return JWSLoader::loadSerializedJsonJWS($json);
92
        }
93
        if (array_key_exists('recipients', $json)) {
94
            return JWELoader::loadSerializedJsonJWE($json);
95
        }
96
    }
97
98
    /**
99
     * @param string $input
100
     *
101
     * @return array
102
     */
103
    private static function convert($input)
104
    {
105
        if (is_array($data = json_decode($input, true))) {
106
            if (array_key_exists('signatures', $data) || array_key_exists('recipients', $data)) {
107
                return $data;
108
            } elseif (array_key_exists('signature', $data)) {
109
                return self::fromFlattenedSerializationSignatureToSerialization($data);
110
            } elseif (array_key_exists('ciphertext', $data)) {
111
                return self::fromFlattenedSerializationRecipientToSerialization($data);
112
            }
113
        } elseif (is_string($input)) {
114
            return self::fromCompactSerializationToSerialization($input);
115
        }
116
        throw new \InvalidArgumentException('Unsupported input');
117
    }
118
119
    /**
120
     * @param $input
121
     *
122
     * @return array
123
     */
124
    private static function fromFlattenedSerializationRecipientToSerialization($input)
125
    {
126
        $recipient = [];
127
        foreach (['header', 'encrypted_key'] as $key) {
128
            if (array_key_exists($key, $input)) {
129
                $recipient[$key] = $input[$key];
130
            }
131
        }
132
        $recipients = [
133
            'ciphertext' => $input['ciphertext'],
134
            'recipients' => [$recipient],
135
        ];
136
        foreach (['ciphertext', 'protected', 'unprotected', 'iv', 'aad', 'tag'] as $key) {
137
            if (array_key_exists($key, $input)) {
138
                $recipients[$key] = $input[$key];
139
            }
140
        }
141
142
        return $recipients;
143
    }
144
145
    /**
146
     * @param $input
147
     *
148
     * @return array
149
     */
150
    private static function fromFlattenedSerializationSignatureToSerialization($input)
151
    {
152
        $signature = [
153
            'signature' => $input['signature'],
154
        ];
155
        foreach (['protected', 'header'] as $key) {
156
            if (array_key_exists($key, $input)) {
157
                $signature[$key] = $input[$key];
158
            }
159
        }
160
161
        $temp = [];
162
        if (!empty($input['payload'])) {
163
            $temp['payload'] = $input['payload'];
164
        }
165
        $temp['signatures'] = [$signature];
166
167
        return $temp;
168
    }
169
170
    /**
171
     * @param string $input
172
     *
173
     * @return array
174
     */
175
    private static function fromCompactSerializationToSerialization($input)
176
    {
177
        $parts = explode('.', $input);
178
        switch (count($parts)) {
179
            case 3:
180
                return self::fromCompactSerializationSignatureToSerialization($parts);
181
            case 5:
182
                return self::fromCompactSerializationRecipientToSerialization($parts);
183
            default:
184
                throw new \InvalidArgumentException('Unsupported input');
185
        }
186
    }
187
188
    /**
189
     * @param array $parts
190
     *
191
     * @return array
192
     */
193
    private static function fromCompactSerializationRecipientToSerialization(array $parts)
194
    {
195
        $recipient = [];
196
        if (!empty($parts[1])) {
197
            $recipient['encrypted_key'] = $parts[1];
198
        }
199
200
        $recipients = [
201
            'recipients' => [$recipient],
202
        ];
203
        foreach ([3 => 'ciphertext', 0 => 'protected', 2 => 'iv', 4 => 'tag'] as $part => $key) {
204
            if (!empty($parts[$part])) {
205
                $recipients[$key] = $parts[$part];
206
            }
207
        }
208
209
        return $recipients;
210
    }
211
212
    /**
213
     * @param array $parts
214
     *
215
     * @return array
216
     */
217
    private static function fromCompactSerializationSignatureToSerialization(array $parts)
218
    {
219
        $temp = [];
220
221
        if (!empty($parts[1])) {
222
            $temp['payload'] = $parts[1];
223
        }
224
        $temp['signatures'] = [[
225
            'protected' => $parts[0],
226
            'signature' => $parts[2],
227
        ]];
228
229
        return $temp;
230
    }
231
}
232