Failed Conditions
Push — v7 ( ae6905...7dd7be )
by Florent
03:50
created

JWE::getSharedHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
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\JWK;
19
use Jose\Component\Core\JWTInterface;
20
21
/**
22
 * Class JWE.
23
 */
24
final class JWE implements JWTInterface
25
{
26
    /**
27
     * @var Recipient[]
28
     */
29
    private $recipients = [];
30
31
    /**
32
     * @var string|null
33
     */
34
    private $ciphertext = null;
35
36
    /**
37
     * @var string|null
38
     */
39
    private $iv = null;
40
41
    /**
42
     * @var string|null
43
     */
44
    private $aad = null;
45
46
    /**
47
     * @var string|null
48
     */
49
    private $tag = null;
50
51
    /**
52
     * @var array
53
     */
54
    private $sharedHeaders = [];
55
56
    /**
57
     * @var array
58
     */
59
    private $sharedProtectedKeaders = [];
60
61
    /**
62
     * @var string|null
63
     */
64
    private $encodedSharedProtectedKeaders = null;
65
66
    /**
67
     * @var mixed|null
68
     */
69
    private $payload = null;
70
71
    /**
72
     * JWE constructor.
73
     *
74
     * @param null|string $ciphertext
75
     * @param null|string $iv
76
     * @param null|string $aad
77
     * @param null|string $tag
78
     * @param array       $sharedHeaders
79
     * @param array       $sharedProtectedKeaders
80
     * @param null|string $encodedSharedProtectedKeaders
81
     * @param mixed|null  $payload
82
     * @param array       $recipients
83
     */
84
    private function __construct(?string $ciphertext = null, ?string $iv = null, ?string $aad = null, ?string $tag = null, array $sharedHeaders = [], array $sharedProtectedKeaders = [], ?string $encodedSharedProtectedKeaders = null, $payload = null, array $recipients = [])
85
    {
86
        $this->ciphertext = $ciphertext;
87
        $this->iv = $iv;
88
        $this->aad = $aad;
89
        $this->tag = $tag;
90
        $this->sharedHeaders = $sharedHeaders;
91
        $this->sharedProtectedKeaders = $sharedProtectedKeaders;
92
        $this->encodedSharedProtectedKeaders = $encodedSharedProtectedKeaders;
93
        $this->payload = $payload;
94
        $this->recipients = $recipients;
95
    }
96
97
    /**
98
     * @return JWE
99
     */
100
    public static function createEmpty(): JWE
101
    {
102
        return new self();
103
    }
104
105
    /**
106
     * @param null|string $ciphertext
107
     * @param null|string $iv
108
     * @param null|string $aad
109
     * @param null|string $tag
110
     * @param array       $sharedHeaders
111
     * @param array       $sharedProtectedKeaders
112
     * @param null|string $encodedSharedProtectedKeaders
113
     * @param array       $recipients
114
     *
115
     * @return JWE
116
     */
117
    public static function create(?string $ciphertext = null, ?string $iv = null, ?string $aad = null, ?string $tag = null, array $sharedHeaders = [], array $sharedProtectedKeaders = [], ?string $encodedSharedProtectedKeaders = null, array $recipients = []): JWE
118
    {
119
        return new self($ciphertext, $iv, $aad, $tag, $sharedHeaders, $sharedProtectedKeaders, $encodedSharedProtectedKeaders, null, $recipients);
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function getPayload()
126
    {
127
        return $this->payload;
128
    }
129
130
    /**
131
     * @param mixed $payload
132
     *
133
     * @return JWE
134
     */
135
    public function withPayload($payload): JWE
136
    {
137
        $jwt = clone $this;
138
        $jwt->payload = $payload;
139
140
        return $jwt;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function getClaim(string $key)
147
    {
148
        if ($this->hasClaim($key)) {
149
            return $this->payload[$key];
150
        }
151
        throw new \InvalidArgumentException(sprintf('The payload does not contain claim "%s".', $key));
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function getClaims(): array
158
    {
159
        if (!$this->hasClaims()) {
160
            throw new \InvalidArgumentException('The payload does not contain claims.');
161
        }
162
163
        return $this->payload;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function hasClaim(string $key): bool
170
    {
171
        return $this->hasClaims() && array_key_exists($key, $this->payload);
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function hasClaims(): bool
178
    {
179
        return is_array($this->payload);
180
    }
181
182
    /**
183
     * Returns the number of recipients associated with the JWS.
184
     *
185
     * @return int
186
     */
187
    public function countRecipients(): int
188
    {
189
        return count($this->recipients);
190
    }
191
192
    /**
193
     * @return bool
194
     */
195
    public function isEncrypted(): bool
196
    {
197
        return null !== $this->getCiphertext();
198
    }
199
200
    /**
201
     * @param array       $recipientHeaders
202
     * @param string|null $encryptedKey
203
     *
204
     * @return JWE
205
     */
206
    public function addRecipient(array $recipientHeaders = [], ?string $encryptedKey = null): JWE
207
    {
208
        $clone = clone $this;
209
        $clone->recipients[] = Recipient::create($recipientHeaders, $encryptedKey);
210
211
        return $clone;
212
    }
213
214
    /**
215
     * Returns the recipients associated with the JWS.
216
     *
217
     * @return Recipient[]
218
     */
219
    public function getRecipients(): array
220
    {
221
        return $this->recipients;
222
    }
223
224
    /**
225
     * @param int $id
226
     *
227
     * @return Recipient
228
     */
229
    public function getRecipient(int $id): Recipient
230
    {
231
        Assertion::keyExists($this->recipients, $id, 'The recipient does not exist.');
232
233
        return $this->recipients[$id];
234
    }
235
236
    /**
237
     * @return string|null The cyphertext
238
     */
239
    public function getCiphertext(): ?string
240
    {
241
        return $this->ciphertext;
242
    }
243
244
    /**
245
     * @param string $ciphertext
246
     *
247
     * @return JWE
248
     */
249
    public function withCiphertext(string $ciphertext): JWE
250
    {
251
        $jwe = clone $this;
252
        $jwe->ciphertext = $ciphertext;
253
254
        return $jwe;
255
    }
256
257
    /**
258
     * @return string|null
259
     */
260
    public function getAAD(): ?string
261
    {
262
        return $this->aad;
263
    }
264
265
    /**
266
     * @param string $aad
267
     *
268
     * @return JWE
269
     */
270
    public function withAAD(string $aad): JWE
271
    {
272
        $jwe = clone $this;
273
        $jwe->aad = $aad;
274
275
        return $jwe;
276
    }
277
278
    /**
279
     * @return string|null
280
     */
281
    public function getIV(): ?string
282
    {
283
        return $this->iv;
284
    }
285
286
    /**
287
     * @param string $iv
288
     *
289
     * @return JWE
290
     */
291
    public function withIV(string $iv): JWE
292
    {
293
        $jwe = clone $this;
294
        $jwe->iv = $iv;
295
296
        return $jwe;
297
    }
298
299
    /**
300
     * @return string|null
301
     */
302
    public function getTag(): ?string
303
    {
304
        return $this->tag;
305
    }
306
307
    /**
308
     * @param string $tag
309
     *
310
     * @return JWE
311
     */
312
    public function withTag(string $tag): JWE
313
    {
314
        $jwe = clone $this;
315
        $jwe->tag = $tag;
316
317
        return $jwe;
318
    }
319
320
    /**
321
     * @return string
322
     */
323
    public function getEncodedSharedProtectedHeaders(): string
324
    {
325
        return $this->encodedSharedProtectedKeaders ?? '';
326
    }
327
328
    /**
329
     * @param string $encodedSharedProtectedKeaders
330
     *
331
     * @return JWE
332
     */
333
    public function withEncodedSharedProtectedHeaders(string $encodedSharedProtectedKeaders): JWE
334
    {
335
        $jwe = clone $this;
336
        $jwe->encodedSharedProtectedKeaders = $encodedSharedProtectedKeaders;
337
338
        return $jwe;
339
    }
340
341
    /**
342
     * @return array
343
     */
344
    public function getSharedProtectedHeaders(): array
345
    {
346
        return $this->sharedProtectedKeaders;
347
    }
348
349
    /**
350
     * @param array $sharedProtectedKeaders
351
     *
352
     * @return JWE
353
     */
354
    public function withSharedProtectedHeaders(array $sharedProtectedKeaders): JWE
355
    {
356
        $jwe = clone $this;
357
        $jwe->sharedProtectedKeaders = $sharedProtectedKeaders;
358
359
        return $jwe;
360
    }
361
362
    /**
363
     * @param string     $key
364
     * @param mixed|null $value
365
     *
366
     * @return JWE
367
     */
368
    public function withSharedProtectedHeader(string $key, $value): JWE
369
    {
370
        $jwe = clone $this;
371
        $jwe->sharedProtectedKeaders[$key] = $value;
372
373
        return $jwe;
374
    }
375
376
    /**
377
     * @param string $key The key
378
     *
379
     * @return mixed|null Header value
380
     */
381
    public function getSharedProtectedHeader(string $key)
382
    {
383
        if ($this->hasSharedProtectedHeader($key)) {
384
            return $this->sharedProtectedKeaders[$key];
385
        }
386
        throw new \InvalidArgumentException(sprintf('The shared protected header "%s" does not exist.', $key));
387
    }
388
389
    /**
390
     * @param string $key The key
391
     *
392
     * @return bool
393
     */
394
    public function hasSharedProtectedHeader(string $key): bool
395
    {
396
        return array_key_exists($key, $this->sharedProtectedKeaders);
397
    }
398
399
    /**
400
     * @param array $sharedHeaders
401
     *
402
     * @return JWE
403
     */
404
    public function withSharedHeaders(array $sharedHeaders): JWE
405
    {
406
        $jwe = clone $this;
407
        $jwe->sharedHeaders = $sharedHeaders;
408
409
        return $jwe;
410
    }
411
412
    /**
413
     * @param string     $key
414
     * @param mixed|null $value
415
     *
416
     * @return JWE
417
     */
418
    public function withSharedHeader(string $key, $value): JWE
419
    {
420
        $jwe = clone $this;
421
        $jwe->sharedHeaders[$key] = $value;
422
423
        return $jwe;
424
    }
425
426
    /**
427
     * @return array
428
     */
429
    public function getSharedHeaders(): array
430
    {
431
        return $this->sharedHeaders;
432
    }
433
434
    /**
435
     * @param string $key The key
436
     *
437
     * @return mixed|null Header value
438
     */
439
    public function getSharedHeader(string $key)
440
    {
441
        if ($this->hasSharedHeader($key)) {
442
            return $this->sharedHeaders[$key];
443
        }
444
        throw new \InvalidArgumentException(sprintf('The shared header "%s" does not exist.', $key));
445
    }
446
447
    /**
448
     * @param string $key The key
449
     *
450
     * @return bool
451
     */
452
    public function hasSharedHeader(string $key): bool
453
    {
454
        return array_key_exists($key, $this->sharedHeaders);
455
    }
456
457
    /**
458
     * @param int $id
459
     *
460
     * @return string
461
     */
462
    public function toCompactJSON(int $id): string
463
    {
464
        $recipient = $this->getRecipient($id);
465
466
        $this->checkHasNoAAD();
467
        $this->checkHasSharedProtectedHeaders();
468
        $this->checkRecipientHasNoHeaders($id);
469
470
        return sprintf(
471
            '%s.%s.%s.%s.%s',
472
            $this->getEncodedSharedProtectedHeaders(),
473
            Base64Url::encode(null === $recipient->getEncryptedKey() ? '' : $recipient->getEncryptedKey()),
474
            Base64Url::encode(null === $this->getIV() ? '' : $this->getIV()),
475
            Base64Url::encode($this->getCiphertext()),
476
            Base64Url::encode(null === $this->getTag() ? '' : $this->getTag())
477
        );
478
    }
479
480
    private function checkHasNoAAD()
481
    {
482
        Assertion::true(empty($this->getAAD()), 'This JWE has AAD and cannot be converted into Compact JSON.');
483
    }
484
485
    /**
486
     * @param int $id
487
     */
488
    private function checkRecipientHasNoHeaders(int $id)
489
    {
490
        Assertion::true(
491
            empty($this->getSharedHeaders()) && empty($this->getRecipient($id)->getHeaders()),
492
            'This JWE has shared headers or recipient headers and cannot be converted into Compact JSON.'
493
        );
494
    }
495
496
    private function checkHasSharedProtectedHeaders()
497
    {
498
        Assertion::notEmpty(
499
            $this->getSharedProtectedHeaders(),
500
            'This JWE does not have shared protected headers and cannot be converted into Compact JSON.'
501
        );
502
    }
503
504
    /**
505
     * @param int $id
506
     *
507
     * @return string
508
     */
509
    public function toFlattenedJSON(int $id): string
510
    {
511
        $recipient = $this->getRecipient($id);
512
513
        $json = $this->getJSONBase();
514
515
        if (!empty($recipient->getHeaders())) {
516
            $json['header'] = $recipient->getHeaders();
517
        }
518
        if (!empty($recipient->getEncryptedKey())) {
519
            $json['encrypted_key'] = Base64Url::encode($recipient->getEncryptedKey());
520
        }
521
522
        return json_encode($json);
523
    }
524
525
    /**
526
     * @return string
527
     */
528
    public function toJSON(): string
529
    {
530
        $json = $this->getJSONBase();
531
        $json['recipients'] = [];
532
533
        foreach ($this->getRecipients() as $recipient) {
534
            $temp = [];
535
            if (!empty($recipient->getHeaders())) {
536
                $temp['header'] = $recipient->getHeaders();
537
            }
538
            if (!empty($recipient->getEncryptedKey())) {
539
                $temp['encrypted_key'] = Base64Url::encode($recipient->getEncryptedKey());
540
            }
541
            $json['recipients'][] = $temp;
542
        }
543
544
        return json_encode($json);
545
    }
546
547
    /**
548
     * @return array
549
     */
550
    private function getJSONBase(): array
551
    {
552
        $json = [
553
            'ciphertext' => Base64Url::encode($this->getCiphertext()),
554
        ];
555
        if (null !== $this->getIV()) {
556
            $json['iv'] = Base64Url::encode($this->getIV());
557
        }
558
        if (null !== $this->getTag()) {
559
            $json['tag'] = Base64Url::encode($this->getTag());
560
        }
561
        if (null !== $this->getAAD()) {
562
            $json['aad'] = Base64Url::encode($this->getAAD());
563
        }
564
        if (!empty($this->getSharedProtectedHeaders())) {
565
            $json['protected'] = $this->getEncodedSharedProtectedHeaders();
566
        }
567
        if (!empty($this->getSharedHeaders())) {
568
            $json['unprotected'] = $this->getSharedHeaders();
569
        }
570
571
        return $json;
572
    }
573
}
574