Failed Conditions
Push — v7 ( 5d1eb6...6055df )
by Florent
02:59
created

JWE::getSharedHeader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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