Failed Conditions
Push — v7 ( 2b71c7...858d6a )
by Florent
02:09
created

Encrypter::createEncrypter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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