Completed
Push — v2.0.x ( b31625...24925e )
by Florent
02:26
created

Loader::populatePayload()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 9.2
cc 4
eloc 7
nc 3
nop 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 Base64Url\Base64Url;
15
use Jose\Object\JWE;
16
use Jose\Object\JWEInterface;
17
use Jose\Object\JWS;
18
use Jose\Object\JWSInterface;
19
use Jose\Object\Recipient;
20
use Jose\Object\RecipientInterface;
21
use Jose\Object\Signature;
22
use Jose\Object\SignatureInterface;
23
24
/**
25
 * Class able to load JWS or JWE.
26
 * JWS object can also be verified.
27
 */
28
final class Loader implements LoaderInterface
29
{
30
    /**
31
     * Loader constructor.
32
     */
33
    private function __construct()
34
    {
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public static function load($input)
41
    {
42
        $json = self::convert($input);
43
        if (array_key_exists('signatures', $json)) {
44
            return self::loadSerializedJsonJWS($json);
45
        }
46
        if (array_key_exists('recipients', $json)) {
47
            return self::loadSerializedJsonJWE($json);
48
        }
49
        throw new \InvalidArgumentException('Unable to load the input');
50
    }
51
52
    /**
53
     * @param array $data
54
     *
55
     * @return \Jose\Object\JWSInterface
56
     */
57
    private static function loadSerializedJsonJWS(array $data)
58
    {
59
        $jws = new JWS();
60
61
        self::populatePayload($jws, $data);
62
63
        foreach ($data['signatures'] as $signature) {
64
            $object = new Signature();
65
            $object = $object->withSignature(Base64Url::decode($signature['signature']));
66
67
            self::populateProtectedHeaders($object, $signature);
68
            self::populateHeaders($object, $signature);
69
70
            $jws = $jws->addSignature($object);
71
        }
72
73
        return $jws;
74
    }
75
76
    /**
77
     * @param \Jose\Object\SignatureInterface $signature
78
     * @param array                           $data
79
     */
80
    private static function populateProtectedHeaders(SignatureInterface &$signature, array $data)
81
    {
82
        if (array_key_exists('protected', $data)) {
83
            $signature = $signature->withEncodedProtectedHeaders($data['protected']);
84
        }
85
    }
86
87
    /**
88
     * @param \Jose\Object\SignatureInterface $signature
89
     * @param array                           $data
90
     */
91
    private static function populateHeaders(SignatureInterface &$signature, array $data)
92
    {
93
        if (array_key_exists('header', $data)) {
94
            $signature = $signature->withHeaders($data['header']);
95
        }
96
    }
97
98
    /**
99
     * @param \Jose\Object\JWSInterface $jws
100
     * @param array                     $data
101
     */
102
    private static function populatePayload(JWSInterface &$jws, array $data)
103
    {
104
        if (array_key_exists('payload', $data)) {
105
            $payload = Base64Url::decode($data['payload']);
106
            $json = json_decode($payload, true);
107
            if (null !== $json && !empty($payload)) {
108
                $payload = $json;
109
            }
110
            $jws = $jws->withPayload($payload);
111
        }
112
    }
113
114
    /**
115
     * @param array $data
116
     *
117
     * @return \Jose\Object\JWEInterface
118
     */
119
    private static function loadSerializedJsonJWE(array $data)
120
    {
121
        $jwe = new JWE();
122
        $jwe = $jwe->withCiphertext(Base64Url::decode($data['ciphertext']));
123
124
        self::populateIV($jwe, $data);
125
        self::populateAAD($jwe, $data);
126
        self::populateTag($jwe, $data);
127
        self::populateSharedProtectedHeaders($jwe, $data);
128
        self::populateSharedHeaders($jwe, $data);
129
130
        foreach ($data['recipients'] as $recipient) {
131
            $object = new Recipient();
132
            self::populateRecipientHeaders($object, $recipient);
133
            self::populateRecipientEncryptedKey($object, $recipient);
134
135
            $jwe = $jwe->addRecipient($object);
136
        }
137
138
        return $jwe;
139
    }
140
141
    /**
142
     * @param \Jose\Object\JWEInterface $jwe
143
     * @param array                     $data
144
     */
145
    private static function populateIV(JWEInterface &$jwe, array $data)
146
    {
147
        if (array_key_exists('iv', $data)) {
148
            $jwe = $jwe->withIV(Base64Url::decode($data['iv']));
149
        }
150
    }
151
152
    /**
153
     * @param \Jose\Object\JWEInterface $jwe
154
     * @param array                     $data
155
     */
156
    private static function populateAAD(JWEInterface &$jwe, array $data)
157
    {
158
        if (array_key_exists('aad', $data)) {
159
            $jwe = $jwe->withAAD(Base64Url::decode($data['aad']));
160
        }
161
    }
162
163
    /**
164
     * @param \Jose\Object\JWEInterface $jwe
165
     * @param array                     $data
166
     */
167
    private static function populateTag(JWEInterface &$jwe, array $data)
168
    {
169
        if (array_key_exists('tag', $data)) {
170
            $jwe = $jwe->withTag(Base64Url::decode($data['tag']));
171
        }
172
    }
173
174
    /**
175
     * @param \Jose\Object\JWEInterface $jwe
176
     * @param array                     $data
177
     */
178
    private static function populateSharedProtectedHeaders(JWEInterface &$jwe, array $data)
179
    {
180
        if (array_key_exists('protected', $data)) {
181
            $jwe = $jwe->withEncodedSharedProtectedHeaders($data['protected']);
182
            $jwe = $jwe->withSharedProtectedHeaders(json_decode(Base64Url::decode($data['protected']), true));
183
        }
184
    }
185
186
    /**
187
     * @param \Jose\Object\JWEInterface $jwe
188
     * @param array                     $data
189
     */
190
    private static function populateSharedHeaders(JWEInterface &$jwe, array $data)
191
    {
192
        if (array_key_exists('unprotected', $data)) {
193
            $jwe = $jwe->withSharedHeaders($data['unprotected']);
194
        }
195
    }
196
197
    /**
198
     * @param \Jose\Object\RecipientInterface $recipient
199
     * @param array                           $data
200
     */
201
    private static function populateRecipientHeaders(RecipientInterface &$recipient, array $data)
202
    {
203
        if (array_key_exists('header', $data)) {
204
            $recipient = $recipient->withHeaders($data['header']);
205
        }
206
    }
207
208
    /**
209
     * @param \Jose\Object\RecipientInterface $recipient
210
     * @param array                           $data
211
     */
212
    private static function populateRecipientEncryptedKey(RecipientInterface &$recipient, array $data)
213
    {
214
        if (array_key_exists('encrypted_key', $data)) {
215
            $recipient = $recipient->withEncryptedKey(Base64Url::decode($data['encrypted_key']));
216
        }
217
    }
218
219
    /**
220
     * @param string $input
221
     *
222
     * @return array
223
     */
224
    private static function convert($input)
225
    {
226
        if (is_array($data = json_decode($input, true))) {
227
            if (array_key_exists('signatures', $data) || array_key_exists('recipients', $data)) {
228
                return $data;
229
            } elseif (array_key_exists('signature', $data)) {
230
                return self::fromFlattenedSerializationSignatureToSerialization($data);
231
            } elseif (array_key_exists('ciphertext', $data)) {
232
                return self::fromFlattenedSerializationRecipientToSerialization($data);
233
            }
234
        } elseif (is_string($input)) {
235
            return self::fromCompactSerializationToSerialization($input);
236
        }
237
        throw new \InvalidArgumentException('Unsupported input');
238
    }
239
240
    /**
241
     * @param $input
242
     *
243
     * @return array
244
     */
245
    private static function fromFlattenedSerializationRecipientToSerialization($input)
246
    {
247
        $recipient = [];
248
        foreach (['header', 'encrypted_key'] as $key) {
249
            if (array_key_exists($key, $input)) {
250
                $recipient[$key] = $input[$key];
251
            }
252
        }
253
        $recipients = [
254
            'ciphertext' => $input['ciphertext'],
255
            'recipients' => [$recipient],
256
        ];
257
        foreach (['ciphertext', 'protected', 'unprotected', 'iv', 'aad', 'tag'] as $key) {
258
            if (array_key_exists($key, $input)) {
259
                $recipients[$key] = $input[$key];
260
            }
261
        }
262
263
        return $recipients;
264
    }
265
266
    /**
267
     * @param $input
268
     *
269
     * @return array
270
     */
271
    private static function fromFlattenedSerializationSignatureToSerialization($input)
272
    {
273
        $signature = [
274
            'signature' => $input['signature'],
275
        ];
276
        foreach (['protected', 'header'] as $key) {
277
            if (array_key_exists($key, $input)) {
278
                $signature[$key] = $input[$key];
279
            }
280
        }
281
282
        $temp = [];
283
        if (!empty($input['payload'])) {
284
            $temp['payload'] = $input['payload'];
285
        }
286
        $temp['signatures'] = [$signature];
287
288
        return $temp;
289
    }
290
291
    /**
292
     * @param string $input
293
     *
294
     * @return array
295
     */
296
    private static function fromCompactSerializationToSerialization($input)
297
    {
298
        $parts = explode('.', $input);
299
        switch (count($parts)) {
300
            case 3:
301
                return self::fromCompactSerializationSignatureToSerialization($parts);
302
            case 5:
303
                return self::fromCompactSerializationRecipientToSerialization($parts);
304
            default:
305
                throw new \InvalidArgumentException('Unsupported input');
306
        }
307
    }
308
309
    /**
310
     * @param array $parts
311
     *
312
     * @return array
313
     */
314
    private static function fromCompactSerializationRecipientToSerialization(array $parts)
315
    {
316
        $recipient = [];
317
        if (!empty($parts[1])) {
318
            $recipient['encrypted_key'] = $parts[1];
319
        }
320
321
        $recipients = [
322
            'recipients' => [$recipient],
323
        ];
324
        foreach ([3 => 'ciphertext', 0 => 'protected', 2 => 'iv', 4 => 'tag'] as $part => $key) {
325
            if (!empty($parts[$part])) {
326
                $recipients[$key] = $parts[$part];
327
            }
328
        }
329
330
        return $recipients;
331
    }
332
333
    /**
334
     * @param array $parts
335
     *
336
     * @return array
337
     */
338
    private static function fromCompactSerializationSignatureToSerialization(array $parts)
339
    {
340
        $temp = [];
341
342
        if (!empty($parts[1])) {
343
            $temp['payload'] = $parts[1];
344
        }
345
        $temp['signatures'] = [[
346
            'protected' => $parts[0],
347
            'signature' => $parts[2],
348
        ]];
349
350
        return $temp;
351
    }
352
}
353