Completed
Push — v2.0.x ( 1446f5...5740fb )
by Florent
02:26
created

Loader::populateRecipientHeaders()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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