Failed Conditions
Push — v7 ( 838dcb...318c5f )
by Florent
04:36
created

getEncryptedKeyFromKeyWrappingAlgorithm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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