Completed
Push — master ( 05e2ef...22b3b9 )
by Florent
02:34
created

JWS::isPayloadEncoded()   A

Complexity

Conditions 2
Paths 2

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 2
eloc 2
nc 2
nop 1
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\Object;
13
14
use Assert\Assertion;
15
use Base64Url\Base64Url;
16
17
/**
18
 * Class JWS.
19
 */
20
final class JWS implements JWSInterface
21
{
22
    use JWT;
23
    
24
    private $is_payload_detached;
25
26
    /**
27
     * @var \Jose\Object\SignatureInterface[]
28
     */
29
    private $signatures = [];
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function isPayloadDetached()
35
    {
36
        return $this->is_payload_detached;
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function withDetachedPayload()
43
    {
44
        $jwt = clone $this;
45
        $jwt->is_payload_detached = true;
46
47
        return $jwt;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function withAttachedPayload()
54
    {
55
        $jwt = clone $this;
56
        $jwt->is_payload_detached = false;
57
58
        return $jwt;
59
    }
60
61
    /**
62
     * @param \Jose\Object\SignatureInterface $signature
63
     *
64
     * @return string|null
65
     */
66
    private function getEncodedPayload(SignatureInterface $signature)
67
    {
68
        if (true === $this->isPayloadDetached()) {
69
            return;
70
        }
71
        $payload = $this->getPayload();
72
        if (!is_string($payload)) {
73
            $payload = json_encode($payload);
74
        }
75
        Assertion::notNull($payload, 'Unsupported payload.');
76
77
        return $this->isPayloadEncoded($signature) ? Base64Url::encode($payload) : $payload;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function getSignatures()
84
    {
85
        return $this->signatures;
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function &getSignature($id)
92
    {
93
        if (isset($this->signatures[$id])) {
94
            return $this->signatures[$id];
95
        }
96
        throw new \InvalidArgumentException('The signature does not exist.');
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function addSignatureInformation(JWKInterface $signature_key, array $protected_headers, array $headers = [])
103
    {
104
        $jws = clone $this;
105
        $jws->signatures[] = Signature::createSignature($signature_key, $protected_headers, $headers);
106
107
        return $jws;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function addSignatureFromLoadedData($signature, $encoded_protected_headers, array $headers)
114
    {
115
        $jws = clone $this;
116
        $jws->signatures[] = Signature::createSignatureFromLoadedData($signature, $encoded_protected_headers, $headers);
117
118
        return $jws;
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function countSignatures()
125
    {
126
        return count($this->signatures);
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function toCompactJSON($id)
133
    {
134
        $signature = $this->getSignature($id);
135
136
        Assertion::true(
137
            empty($signature->getHeaders()),
138
            'The signature contains unprotected headers and cannot be converted into compact JSON'
139
        );
140
        Assertion::true($this->isPayloadEncoded($signature) || empty($this->getEncodedPayload($signature)), 'Unable to convert the JWS with non-encoded payload.');
141
142
        return sprintf(
143
            '%s.%s.%s',
144
            $signature->getEncodedProtectedHeaders(),
145
            $this->getEncodedPayload($signature),
146
            Base64Url::encode($signature->getSignature())
147
        );
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function toFlattenedJSON($id)
154
    {
155
        $signature = $this->getSignature($id);
156
157
        $data = [];
158
        $values = [
159
            'payload'   => $this->getEncodedPayload($signature),
160
            'protected' => $signature->getEncodedProtectedHeaders(),
161
            'header'    => $signature->getHeaders(),
162
        ];
163
164
        foreach ($values as $key => $value) {
165
            if (!empty($value)) {
166
                $data[$key] = $value;
167
            }
168
        }
169
        $data['signature'] = Base64Url::encode($signature->getSignature());
170
171
        return json_encode($data);
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function toJSON()
178
    {
179
        Assertion::greaterThan($this->countSignatures(), 0, 'No signature.');
180
181
        $data = [];
182
        $this->checkPayloadEncoding();
183
184
        if (false === $this->isPayloadDetached()) {
185
            $data['payload'] = $this->getEncodedPayload($this->getSignature(0));
186
        }
187
188
        $data['signatures'] = [];
189
        foreach ($this->getSignatures() as $signature) {
190
            $tmp = ['signature' => Base64Url::encode($signature->getSignature())];
191
            $values = [
192
                'protected' => $signature->getEncodedProtectedHeaders(),
193
                'header'    => $signature->getHeaders(),
194
            ];
195
196
            foreach ($values as $key => $value) {
197
                if (!empty($value)) {
198
                    $tmp[$key] = $value;
199
                }
200
            }
201
            $data['signatures'][] = $tmp;
202
        }
203
204
        return json_encode($data);
205
    }
206
207
    /**
208
     * @param \Jose\Object\SignatureInterface $signature
209
     *
210
     * @return bool
211
     */
212
    private function isPayloadEncoded(SignatureInterface $signature)
213
    {
214
        return !$signature->hasProtectedHeader('b64') || true === $signature->getProtectedHeader('b64');
215
    }
216
217
    private function checkPayloadEncoding()
218
    {
219
        $is_encoded = null;
220
        foreach($this->getSignatures() as $signature) {
221
            if (null === $is_encoded) {
222
                $is_encoded = $this->isPayloadEncoded($signature);
223
            }
224
            Assertion::eq($is_encoded, $this->isPayloadEncoded($signature), 'Foreign payload encoding detected. The JWS cannot be converted.');
225
        }
226
    }
227
}
228