ba015e8a179a6e0dd7d9f711e6aa618ddcf10c44
[GitHub/WoltLab/WCF.git] /
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Jose\Component\Encryption\Serializer;
6
7 use InvalidArgumentException;
8 use Jose\Component\Core\Util\JsonConverter;
9 use Jose\Component\Encryption\JWE;
10 use Jose\Component\Encryption\Recipient;
11 use LogicException;
12 use ParagonIE\ConstantTime\Base64UrlSafe;
13 use Throwable;
14 use function count;
15 use function is_array;
16
17 final class CompactSerializer implements JWESerializer
18 {
19 public const NAME = 'jwe_compact';
20
21 public function displayName(): string
22 {
23 return 'JWE Compact';
24 }
25
26 public function name(): string
27 {
28 return self::NAME;
29 }
30
31 public function serialize(JWE $jwe, ?int $recipientIndex = null): string
32 {
33 if ($recipientIndex === null) {
34 $recipientIndex = 0;
35 }
36 $recipient = $jwe->getRecipient($recipientIndex);
37
38 $this->checkHasNoAAD($jwe);
39 $this->checkHasSharedProtectedHeader($jwe);
40 $this->checkRecipientHasNoHeader($jwe, $recipientIndex);
41
42 return sprintf(
43 '%s.%s.%s.%s.%s',
44 $jwe->getEncodedSharedProtectedHeader(),
45 Base64UrlSafe::encodeUnpadded($recipient->getEncryptedKey() ?? ''),
46 Base64UrlSafe::encodeUnpadded($jwe->getIV() ?? ''),
47 Base64UrlSafe::encodeUnpadded($jwe->getCiphertext() ?? ''),
48 Base64UrlSafe::encodeUnpadded($jwe->getTag() ?? '')
49 );
50 }
51
52 public function unserialize(string $input): JWE
53 {
54 $parts = explode('.', $input);
55 if (count($parts) !== 5) {
56 throw new InvalidArgumentException('Unsupported input');
57 }
58
59 try {
60 $encodedSharedProtectedHeader = $parts[0];
61 $sharedProtectedHeader = JsonConverter::decode(
62 Base64UrlSafe::decodeNoPadding($encodedSharedProtectedHeader)
63 );
64 if (! is_array($sharedProtectedHeader)) {
65 throw new InvalidArgumentException('Unsupported input.');
66 }
67 $encryptedKey = $parts[1] === '' ? null : Base64UrlSafe::decodeNoPadding($parts[1]);
68 $iv = Base64UrlSafe::decodeNoPadding($parts[2]);
69 $ciphertext = Base64UrlSafe::decodeNoPadding($parts[3]);
70 $tag = Base64UrlSafe::decodeNoPadding($parts[4]);
71
72 return new JWE(
73 $ciphertext,
74 $iv,
75 $tag,
76 null,
77 [],
78 $sharedProtectedHeader,
79 $encodedSharedProtectedHeader,
80 [new Recipient([], $encryptedKey)]
81 );
82 } catch (Throwable $throwable) {
83 throw new InvalidArgumentException('Unsupported input', $throwable->getCode(), $throwable);
84 }
85 }
86
87 private function checkHasNoAAD(JWE $jwe): void
88 {
89 if ($jwe->getAAD() !== null) {
90 throw new LogicException('This JWE has AAD and cannot be converted into Compact JSON.');
91 }
92 }
93
94 private function checkRecipientHasNoHeader(JWE $jwe, int $id): void
95 {
96 if (count($jwe->getSharedHeader()) !== 0 || count($jwe->getRecipient($id)->getHeader()) !== 0) {
97 throw new LogicException(
98 'This JWE has shared header parameters or recipient header parameters and cannot be converted into Compact JSON.'
99 );
100 }
101 }
102
103 private function checkHasSharedProtectedHeader(JWE $jwe): void
104 {
105 if (count($jwe->getSharedProtectedHeader()) === 0) {
106 throw new LogicException(
107 'This JWE does not have shared protected header parameters and cannot be converted into Compact JSON.'
108 );
109 }
110 }
111 }