Failed Conditions
Push — v7 ( 044b56...cc2410 )
by Florent
02:06
created

JWE::withAAD()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
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 Assert\Assertion;
17
use Base64Url\Base64Url;
18
use Jose\Component\Core\JWTInterface;
19
20
/**
21
 * Class JWE.
22
 */
23
final class JWE implements JWTInterface
24
{
25
    /**
26
     * @var Recipient[]
27
     */
28
    private $recipients = [];
29
30
    /**
31
     * @var string|null
32
     */
33
    private $ciphertext = null;
34
35
    /**
36
     * @var string|null
37
     */
38
    private $iv = null;
39
40
    /**
41
     * @var string|null
42
     */
43
    private $aad = null;
44
45
    /**
46
     * @var string|null
47
     */
48
    private $tag = null;
49
50
    /**
51
     * @var array
52
     */
53
    private $sharedHeaders = [];
54
55
    /**
56
     * @var array
57
     */
58
    private $sharedProtectedHeaders = [];
59
60
    /**
61
     * @var string|null
62
     */
63
    private $encodedSharedProtectedHeaders = null;
64
65
    /**
66
     * @var mixed|null
67
     */
68
    private $payload = null;
69
70
    /**
71
     * JWE constructor.
72
     *
73
     * @param string      $ciphertext
74
     * @param null|string $iv
75
     * @param null|string $aad
76
     * @param null|string $tag
77
     * @param array       $sharedHeaders
78
     * @param array       $sharedProtectedHeaders
79
     * @param null|string $encodedSharedProtectedHeaders
80
     * @param array       $recipients
81
     */
82
    private function __construct(string $ciphertext, ?string $iv = null, ?string $aad = null, ?string $tag = null, array $sharedHeaders = [], array $sharedProtectedHeaders = [], ?string $encodedSharedProtectedHeaders = null, array $recipients = [])
83
    {
84
        $this->ciphertext = $ciphertext;
85
        $this->iv = $iv;
86
        $this->aad = $aad;
87
        $this->tag = $tag;
88
        $this->sharedHeaders = $sharedHeaders;
89
        $this->sharedProtectedHeaders = $sharedProtectedHeaders;
90
        $this->encodedSharedProtectedHeaders = $encodedSharedProtectedHeaders;
91
        $this->recipients = $recipients;
92
    }
93
94
    /**
95
     * @param string      $ciphertext
96
     * @param null|string $iv
97
     * @param null|string $aad
98
     * @param null|string $tag
99
     * @param array       $sharedHeaders
100
     * @param array       $sharedProtectedHeaders
101
     * @param null|string $encodedSharedProtectedHeaders
102
     * @param array       $recipients
103
     *
104
     * @return JWE
105
     */
106
    public static function create(string $ciphertext, ?string $iv = null, ?string $aad = null, ?string $tag = null, array $sharedHeaders = [], array $sharedProtectedHeaders = [], ?string $encodedSharedProtectedHeaders = null, array $recipients = []): JWE
107
    {
108
        return new self($ciphertext, $iv, $aad, $tag, $sharedHeaders, $sharedProtectedHeaders, $encodedSharedProtectedHeaders, $recipients);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function getPayload()
115
    {
116
        return $this->payload;
117
    }
118
119
    /**
120
     * @param $payload
121
     *
122
     * @return JWE
123
     */
124
    public function withPayload($payload): JWE
125
    {
126
        $clone = clone $this;
127
        $clone->payload = $payload;
128
129
        return $clone;
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function getClaim(string $key)
136
    {
137
        if ($this->hasClaim($key)) {
138
            return $this->payload[$key];
139
        }
140
        throw new \InvalidArgumentException(sprintf('The payload does not contain claim "%s".', $key));
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function getClaims(): array
147
    {
148
        if (!$this->hasClaims()) {
149
            throw new \InvalidArgumentException('The payload does not contain claims.');
150
        }
151
152
        return $this->payload;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158
    public function hasClaim(string $key): bool
159
    {
160
        return $this->hasClaims() && array_key_exists($key, $this->payload);
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function hasClaims(): bool
167
    {
168
        return is_array($this->payload);
169
    }
170
171
    /**
172
     * Returns the number of recipients associated with the JWS.
173
     *
174
     * @return int
175
     */
176
    public function countRecipients(): int
177
    {
178
        return count($this->recipients);
179
    }
180
181
    /**
182
     * @return bool
183
     */
184
    public function isEncrypted(): bool
185
    {
186
        return null !== $this->getCiphertext();
187
    }
188
189
    /**
190
     * Returns the recipients associated with the JWS.
191
     *
192
     * @return Recipient[]
193
     */
194
    public function getRecipients(): array
195
    {
196
        return $this->recipients;
197
    }
198
199
    /**
200
     * @param int $id
201
     *
202
     * @return Recipient
203
     */
204
    public function getRecipient(int $id): Recipient
205
    {
206
        Assertion::keyExists($this->recipients, $id, 'The recipient does not exist.');
207
208
        return $this->recipients[$id];
209
    }
210
211
    /**
212
     * @return string|null The cyphertext
213
     */
214
    public function getCiphertext(): ?string
215
    {
216
        return $this->ciphertext;
217
    }
218
219
    /**
220
     * @return string|null
221
     */
222
    public function getAAD(): ?string
223
    {
224
        return $this->aad;
225
    }
226
227
    /**
228
     * @return string|null
229
     */
230
    public function getIV(): ?string
231
    {
232
        return $this->iv;
233
    }
234
235
    /**
236
     * @return string|null
237
     */
238
    public function getTag(): ?string
239
    {
240
        return $this->tag;
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getEncodedSharedProtectedHeaders(): string
247
    {
248
        return $this->encodedSharedProtectedHeaders ?? '';
249
    }
250
251
    /**
252
     * @return array
253
     */
254
    public function getSharedProtectedHeaders(): array
255
    {
256
        return $this->sharedProtectedHeaders;
257
    }
258
259
    /**
260
     * @param string $key The key
261
     *
262
     * @return mixed|null Header value
263
     */
264
    public function getSharedProtectedHeader(string $key)
265
    {
266
        if ($this->hasSharedProtectedHeader($key)) {
267
            return $this->sharedProtectedHeaders[$key];
268
        }
269
        throw new \InvalidArgumentException(sprintf('The shared protected header "%s" does not exist.', $key));
270
    }
271
272
    /**
273
     * @param string $key The key
274
     *
275
     * @return bool
276
     */
277
    public function hasSharedProtectedHeader(string $key): bool
278
    {
279
        return array_key_exists($key, $this->sharedProtectedHeaders);
280
    }
281
282
    /**
283
     * @return array
284
     */
285
    public function getSharedHeaders(): array
286
    {
287
        return $this->sharedHeaders;
288
    }
289
290
    /**
291
     * @param string $key The key
292
     *
293
     * @return mixed|null Header value
294
     */
295
    public function getSharedHeader(string $key)
296
    {
297
        if ($this->hasSharedHeader($key)) {
298
            return $this->sharedHeaders[$key];
299
        }
300
        throw new \InvalidArgumentException(sprintf('The shared header "%s" does not exist.', $key));
301
    }
302
303
    /**
304
     * @param string $key The key
305
     *
306
     * @return bool
307
     */
308
    public function hasSharedHeader(string $key): bool
309
    {
310
        return array_key_exists($key, $this->sharedHeaders);
311
    }
312
313
    /**
314
     * @param int $id
315
     *
316
     * @return string
317
     */
318
    public function toCompactJSON(int $id): string
319
    {
320
        $recipient = $this->getRecipient($id);
321
322
        $this->checkHasNoAAD();
323
        $this->checkHasSharedProtectedHeaders();
324
        $this->checkRecipientHasNoHeaders($id);
325
326
        return sprintf(
327
            '%s.%s.%s.%s.%s',
328
            $this->getEncodedSharedProtectedHeaders(),
329
            Base64Url::encode(null === $recipient->getEncryptedKey() ? '' : $recipient->getEncryptedKey()),
330
            Base64Url::encode(null === $this->getIV() ? '' : $this->getIV()),
331
            Base64Url::encode($this->getCiphertext()),
332
            Base64Url::encode(null === $this->getTag() ? '' : $this->getTag())
333
        );
334
    }
335
336
    private function checkHasNoAAD()
337
    {
338
        Assertion::true(empty($this->getAAD()), 'This JWE has AAD and cannot be converted into Compact JSON.');
339
    }
340
341
    /**
342
     * @param int $id
343
     */
344
    private function checkRecipientHasNoHeaders(int $id)
345
    {
346
        Assertion::true(
347
            empty($this->getSharedHeaders()) && empty($this->getRecipient($id)->getHeaders()),
348
            'This JWE has shared headers or recipient headers and cannot be converted into Compact JSON.'
349
        );
350
    }
351
352
    private function checkHasSharedProtectedHeaders()
353
    {
354
        Assertion::notEmpty(
355
            $this->getSharedProtectedHeaders(),
356
            'This JWE does not have shared protected headers and cannot be converted into Compact JSON.'
357
        );
358
    }
359
360
    /**
361
     * @param int $id
362
     *
363
     * @return string
364
     */
365
    public function toFlattenedJSON(int $id): string
366
    {
367
        $recipient = $this->getRecipient($id);
368
369
        $json = $this->getJSONBase();
370
371
        if (!empty($recipient->getHeaders())) {
372
            $json['header'] = $recipient->getHeaders();
373
        }
374
        if (!empty($recipient->getEncryptedKey())) {
375
            $json['encrypted_key'] = Base64Url::encode($recipient->getEncryptedKey());
376
        }
377
378
        return json_encode($json);
379
    }
380
381
    /**
382
     * @return string
383
     */
384
    public function toJSON(): string
385
    {
386
        $json = $this->getJSONBase();
387
        $json['recipients'] = [];
388
389
        foreach ($this->getRecipients() as $recipient) {
390
            $temp = [];
391
            if (!empty($recipient->getHeaders())) {
392
                $temp['header'] = $recipient->getHeaders();
393
            }
394
            if (!empty($recipient->getEncryptedKey())) {
395
                $temp['encrypted_key'] = Base64Url::encode($recipient->getEncryptedKey());
396
            }
397
            $json['recipients'][] = $temp;
398
        }
399
400
        return json_encode($json);
401
    }
402
403
    /**
404
     * @return array
405
     */
406
    private function getJSONBase(): array
407
    {
408
        $json = [
409
            'ciphertext' => Base64Url::encode($this->getCiphertext()),
410
        ];
411
        if (null !== $this->getIV()) {
412
            $json['iv'] = Base64Url::encode($this->getIV());
413
        }
414
        if (null !== $this->getTag()) {
415
            $json['tag'] = Base64Url::encode($this->getTag());
416
        }
417
        if (null !== $this->getAAD()) {
418
            $json['aad'] = Base64Url::encode($this->getAAD());
419
        }
420
        if (!empty($this->getSharedProtectedHeaders())) {
421
            $json['protected'] = $this->getEncodedSharedProtectedHeaders();
422
        }
423
        if (!empty($this->getSharedHeaders())) {
424
            $json['unprotected'] = $this->getSharedHeaders();
425
        }
426
427
        return $json;
428
    }
429
}
430