Failed Conditions
Push — v7 ( 318c5f...2b71c7 )
by Florent
03:43
created

Encrypter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 3
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;
13
14
use Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Jose\Algorithm\ContentEncryptionAlgorithmInterface;
17
use Jose\Algorithm\JWAManager;
18
use Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface;
19
use Jose\Algorithm\KeyEncryption\KeyEncryptionInterface;
20
use Jose\Algorithm\KeyEncryption\KeyWrappingInterface;
21
use Jose\Algorithm\KeyEncryptionAlgorithmInterface;
22
use Jose\Compression\CompressionInterface;
23
use Jose\Compression\CompressionManager;
24
use Jose\Object\JWEInterface;
25
use Jose\Object\JWKInterface;
26
use Jose\Object\Recipient;
27
use Jose\Object\RecipientInterface;
28
29
final class Encrypter
30
{
31
    /**
32
     * @var JWAManager
33
     */
34
    private $jwaManager;
35
36
    /**
37
     * @var CompressionManager
38
     */
39
    private $compressionManager;
40
41
    use Behaviour\HasKeyChecker;
42
    use Behaviour\CommonCipheringMethods;
43
    use Behaviour\EncrypterTrait;
44
45
    /**
46
     * @param array $key_encryption_algorithms
47
     * @param array $content_encryption_algorithms
48
     * @param array $compression_methods
49
     * @return Encrypter
50
     */
51
    public static function createEncrypter(array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF', 'ZLIB', 'GZ']): Encrypter
52
    {
53
        $encrypter = new self($key_encryption_algorithms, $content_encryption_algorithms, $compression_methods);
54
55
        return $encrypter;
56
    }
57
58
    /**
59
     * Decrypter constructor.
60
     *
61
     * @param string[]|KeyEncryptionAlgorithmInterface[]     $key_encryption_algorithms
62
     * @param string[]|ContentEncryptionAlgorithmInterface[] $content_encryption_algorithms
63
     * @param string[]|CompressionInterface[]              $compression_methods
64
     */
65
    public function __construct(array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods)
66
    {
67
        $this->setKeyEncryptionAlgorithms($key_encryption_algorithms);
68
        $this->setContentEncryptionAlgorithms($content_encryption_algorithms);
69
        $this->setCompressionMethods($compression_methods);
70
        $this->jwaManager = Factory\AlgorithmManagerFactory::createAlgorithmManager(array_merge($key_encryption_algorithms, $content_encryption_algorithms));
71
        $this->compressionManager = Factory\CompressionManagerFactory::createCompressionManager($compression_methods);
72
    }
73
74
    /**
75
     * @param JWEInterface $jwe
76
     */
77
    public function encrypt(JWEInterface &$jwe)
78
    {
79
        Assertion::false($jwe->isEncrypted(), 'The JWE is already encrypted.');
80
        Assertion::greaterThan($jwe->countRecipients(), 0, 'The JWE does not contain recipient.');
81
        $additional_headers = [];
82
        $nb_recipients = $jwe->countRecipients();
83
        $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($jwe);
84
        $compression_method = $this->getCompressionMethod($jwe);
85
        $key_management_mode = $this->getKeyManagementMode($jwe);
86
        $cek = $this->determineCEK($jwe, $content_encryption_algorithm, $key_management_mode, $additional_headers);
87
88
        for ($i = 0; $i < $nb_recipients; $i++) {
89
            $this->processRecipient($jwe, $jwe->getRecipient($i), $cek, $content_encryption_algorithm, $additional_headers);
90
        }
91
92
        if (!empty($additional_headers) && 1 === $jwe->countRecipients()) {
93
            $jwe = $jwe->withSharedProtectedHeaders(array_merge($jwe->getSharedProtectedHeaders(), $additional_headers));
94
        }
95
96
        $iv_size = $content_encryption_algorithm->getIVSize();
97
        $iv = $this->createIV($iv_size);
98
99
        $this->encryptJWE($jwe, $content_encryption_algorithm, $cek, $iv, $compression_method);
100
    }
101
102
    /**
103
     * @param JWEInterface                           $jwe
104
     * @param RecipientInterface                     $recipient
105
     * @param string                                              $cek
106
     * @param ContentEncryptionAlgorithmInterface $content_encryption_algorithm
107
     * @param array                                               $additional_headers
108
     */
109
    private function processRecipient(JWEInterface $jwe, RecipientInterface &$recipient, $cek, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers)
110
    {
111
        if (null === $recipient->getRecipientKey()) {
112
            return;
113
        }
114
        $complete_headers = array_merge($jwe->getSharedProtectedHeaders(), $jwe->getSharedHeaders(), $recipient->getHeaders());
115
        $key_encryption_algorithm = $this->findKeyEncryptionAlgorithm($complete_headers);
116
        $this->checkKeys($key_encryption_algorithm, $content_encryption_algorithm, $recipient->getRecipientKey());
117
        $encrypted_content_encryption_key = $this->getEncryptedKey($complete_headers, $cek, $key_encryption_algorithm, $content_encryption_algorithm, $additional_headers, $recipient->getRecipientKey());
118
        $recipient_headers = $recipient->getHeaders();
119
        if (!empty($additional_headers) && 1 !== $jwe->countRecipients()) {
120
            $recipient_headers = array_merge($recipient_headers, $additional_headers);
121
            $additional_headers = [];
122
        }
123
124
        $recipient = Recipient::createRecipientFromLoadedJWE($recipient_headers, $encrypted_content_encryption_key);
125
    }
126
127
    /**
128
     * @param JWEInterface                           $jwe
129
     * @param ContentEncryptionAlgorithmInterface $content_encryption_algorithm
130
     * @param string                                              $cek
131
     * @param string                                              $iv
132
     * @param CompressionInterface|null         $compression_method
133
     */
134
    private function encryptJWE(JWEInterface &$jwe, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, $cek, $iv, Compression\CompressionInterface $compression_method = null)
135
    {
136
        if (!empty($jwe->getSharedProtectedHeaders())) {
137
            $jwe = $jwe->withEncodedSharedProtectedHeaders(Base64Url::encode(json_encode($jwe->getSharedProtectedHeaders())));
138
        }
139
140
        $tag = null;
141
        $payload = $this->preparePayload($jwe->getPayload(), $compression_method);
142
        $aad = null === $jwe->getAAD() ? null : Base64Url::encode($jwe->getAAD());
143
        $ciphertext = $content_encryption_algorithm->encryptContent($payload, $cek, $iv, $aad, $jwe->getEncodedSharedProtectedHeaders(), $tag);
144
        $jwe = $jwe->withCiphertext($ciphertext);
145
        $jwe = $jwe->withIV($iv);
146
147
        if (null !== $tag) {
148
            $jwe = $jwe->withTag($tag);
149
        }
150
    }
151
152
    /**
153
     * @param string                                      $payload
154
     * @param CompressionInterface|null $compression_method
155
     *
156
     * @return string
157
     */
158
    private function preparePayload($payload, Compression\CompressionInterface $compression_method = null): string
159
    {
160
        $prepared = is_string($payload) ? $payload : json_encode($payload);
161
        Assertion::notNull($prepared, 'The payload is empty or cannot encoded into JSON.');
162
163
        if (null === $compression_method) {
164
            return $prepared;
165
        }
166
        $compressed_payload = $compression_method->compress($prepared);
167
        Assertion::string($compressed_payload, 'Compression failed.');
168
169
        return $compressed_payload;
170
    }
171
172
    /**
173
     * @param array                                               $complete_headers
174
     * @param string                                              $cek
175
     * @param KeyEncryptionAlgorithmInterface     $key_encryption_algorithm
176
     * @param ContentEncryptionAlgorithmInterface $content_encryption_algorithm
177
     * @param JWKInterface                           $recipient_key
178
     * @param array                                               $additional_headers
179
     *
180
     * @return string|null
181
     */
182
    private function getEncryptedKey(array $complete_headers, $cek, KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, JWKInterface $recipient_key): ?string
183
    {
184
        if ($key_encryption_algorithm instanceof KeyEncryptionInterface) {
185
            return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers);
186
        } elseif ($key_encryption_algorithm instanceof KeyWrappingInterface) {
187
            return $this->getEncryptedKeyFromKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $recipient_key, $additional_headers);
188
        } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) {
189
            return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($complete_headers, $cek, $key_encryption_algorithm, $content_encryption_algorithm, $additional_headers, $recipient_key);
190
        }
191
192
        return null;
193
    }
194
195
    /**
196
     * @param array                                                       $complete_headers
197
     * @param string                                                      $cek
198
     * @param KeyAgreementWrappingInterface $key_encryption_algorithm
199
     * @param ContentEncryptionAlgorithmInterface         $content_encryption_algorithm
200
     * @param array                                                       $additional_headers
201
     * @param JWKInterface                                   $recipient_key
202
     *
203
     * @return string
204
     */
205
    private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $complete_headers, $cek, KeyAgreementWrappingInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, array &$additional_headers, JWKInterface $recipient_key): string
206
    {
207
        $jwt_cek = $key_encryption_algorithm->wrapAgreementKey($recipient_key, $cek, $content_encryption_algorithm->getCEKSize(), $complete_headers, $additional_headers);
208
209
        return $jwt_cek;
210
    }
211
212
    /**
213
     * @param array                                                $complete_headers
214
     * @param string                                               $cek
215
     * @param KeyEncryptionInterface $key_encryption_algorithm
216
     * @param JWKInterface                            $recipient_key
217
     * @param array                                                $additional_headers
218
     *
219
     * @return string
220
     */
221
    private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $complete_headers, string $cek, KeyEncryptionInterface $key_encryption_algorithm, JWKInterface $recipient_key, array &$additional_headers)
222
    {
223
        return $key_encryption_algorithm->encryptKey($recipient_key, $cek, $complete_headers, $additional_headers);
224
    }
225
226
    /**
227
     * @param array                                              $complete_headers
228
     * @param string                                             $cek
229
     * @param KeyWrappingInterface $key_encryption_algorithm
230
     * @param JWKInterface                          $recipient_key
231
     * @param array                                              $additional_headers
232
     *
233
     * @return string
234
     */
235
    private function getEncryptedKeyFromKeyWrappingAlgorithm(array $complete_headers, string $cek, KeyWrappingInterface $key_encryption_algorithm, JWKInterface $recipient_key, &$additional_headers)
236
    {
237
        return $key_encryption_algorithm->wrapKey($recipient_key, $cek, $complete_headers, $additional_headers);
238
    }
239
240
    /**
241
     * @return JWAManager
242
     */
243
    protected function getJWAManager(): JWAManager
244
    {
245
        return $this->jwaManager;
246
    }
247
248
    /**
249
     * @return CompressionManager
250
     */
251
    protected function getCompressionManager(): CompressionManager
252
    {
253
        return $this->compressionManager;
254
    }
255
}
256