Failed Conditions
Push — v7 ( 0b8993...932046 )
by Florent
03:54
created

JWEParser::loadSerializedJsonJWE()   B

Complexity

Conditions 8
Paths 80

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 7.7777
c 0
b 0
f 0
cc 8
eloc 14
nc 80
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Encryption;
15
16
use Base64Url\Base64Url;
17
18
/**
19
 * Class able to parse JWE.
20
 */
21
final class JWEParser
22
{
23
    /**
24
     * Load data and try to return a JWS object, a JWE object or a list of these objects.
25
     * If the result is a JWE (list), nothing is decrypted and method `decrypt` must be executed
26
     * If the result is a JWS (list), no signature is verified and method `verifySignature` must be executed.
27
     *
28
     * @param string $input A string that represents a JSON Web Token message
29
     *
30
     * @return JWE if the data has been loaded
31
     */
32
    public static function parse(string $input): JWE
33
    {
34
        $json = self::convert($input);
35
36
        return self::loadSerializedJsonJWE($json);
37
    }
38
39
    /**
40
     * @param string $input
41
     *
42
     * @return array
43
     */
44
    private static function convert(string $input): array
45
    {
46
        if (is_array($data = json_decode($input, true))) {
47
            if (array_key_exists('recipients', $data)) {
48
                return $data;
49
            } elseif (array_key_exists('ciphertext', $data)) {
50
                return self::fromFlattenedSerializationRecipientToSerialization($data);
51
            }
52
        } elseif (is_string($input)) {
53
            return self::fromCompactSerializationToSerialization($input);
54
        }
55
56
        throw new \InvalidArgumentException('Unsupported input');
57
    }
58
59
    /**
60
     * @param array $input
61
     *
62
     * @return array
63
     */
64
    private static function fromFlattenedSerializationRecipientToSerialization(array $input): array
65
    {
66
        $recipient = [];
67
        $recipient = array_merge(
68
            $recipient,
69
            array_intersect_key($input, array_flip(['header', 'encrypted_key']))
70
        );
71
        $recipients = [
72
            'ciphertext' => $input['ciphertext'],
73
            'recipients' => [$recipient],
74
        ];
75
        $recipients = array_merge(
76
            $recipients,
77
            array_intersect_key($input, array_flip(['protected', 'unprotected', 'iv', 'aad', 'tag']))
78
        );
79
80
        return $recipients;
81
    }
82
83
    /**
84
     * @param string $input
85
     *
86
     * @return array
87
     */
88
    private static function fromCompactSerializationToSerialization(string $input): array
89
    {
90
        $parts = explode('.', $input);
91
        switch (count($parts)) {
92
            case 5:
93
                return self::fromCompactSerializationRecipientToSerialization($parts);
94
            default:
95
                throw new \InvalidArgumentException('Unsupported input');
96
        }
97
    }
98
99
    /**
100
     * @param array $parts
101
     *
102
     * @return array
103
     */
104
    private static function fromCompactSerializationRecipientToSerialization(array $parts): array
105
    {
106
        $recipient = [];
107
        if (!empty($parts[1])) {
108
            $recipient['encrypted_key'] = $parts[1];
109
        }
110
111
        $recipients = [
112
            'recipients' => [$recipient],
113
        ];
114
        foreach ([0 => 'protected', 2 => 'iv', 3 => 'ciphertext', 4 => 'tag'] as $part => $key) {
115
            if (!empty($parts[$part])) {
116
                $recipients[$key] = $parts[$part];
117
            }
118
        }
119
120
        return $recipients;
121
    }
122
123
    /**
124
     * @param array $data
125
     *
126
     * @return JWE
127
     */
128
    private static function loadSerializedJsonJWE(array $data): JWE
129
    {
130
        $ciphertext = Base64Url::decode($data['ciphertext']);
131
        $iv = Base64Url::decode($data['iv']);
132
        $tag = Base64Url::decode($data['tag']);
133
        $aad = array_key_exists('aad', $data) ? Base64Url::decode($data['aad']) : null;
134
        $encodedSharedProtectedHeader = array_key_exists('protected', $data) ? $data['protected'] : null;
135
        $sharedProtectedHeader = $encodedSharedProtectedHeader ? json_decode(Base64Url::decode($encodedSharedProtectedHeader), true) : [];
136
        $sharedHeader = array_key_exists('unprotected', $data) ? $data['unprotected'] : [];
137
        $recipients = [];
138
        foreach ($data['recipients'] as $recipient) {
139
            $encryptedKey = array_key_exists('encrypted_key', $recipient) ? Base64Url::decode($recipient['encrypted_key']) : null;
140
            $header = array_key_exists('header', $recipient) ? $recipient['header'] : [];
141
            $recipients[] = Recipient::create($header, $encryptedKey);
142
        }
143
144
        return JWE::create($ciphertext, $iv, $tag, $aad, $sharedHeader, $sharedProtectedHeader, $encodedSharedProtectedHeader, $recipients);
145
    }
146
}
147