|
23 | 23 | */ |
24 | 24 | class OpenSSLHandler extends BaseHandler |
25 | 25 | { |
| 26 | + /** |
| 27 | + * Encryption key info. |
| 28 | + * This setting is only used by OpenSSLHandler. |
| 29 | + * |
| 30 | + * Set to 'encryption' for CI3 Encryption compatibility. |
| 31 | + */ |
| 32 | + public string $encryptKeyInfo = ''; |
| 33 | + |
| 34 | + /** |
| 35 | + * Authentication key info. |
| 36 | + * This setting is only used by OpenSSLHandler. |
| 37 | + * |
| 38 | + * Set to 'authentication' for CI3 Encryption compatibility. |
| 39 | + */ |
| 40 | + public string $authKeyInfo = ''; |
| 41 | + |
26 | 42 | /** |
27 | 43 | * HMAC digest to use |
28 | 44 | * |
@@ -56,34 +72,11 @@ class OpenSSLHandler extends BaseHandler |
56 | 72 | */ |
57 | 73 | protected $key = ''; |
58 | 74 |
|
59 | | - /** |
60 | | - * List of previous keys for fallback decryption. |
61 | | - * |
62 | | - * @var list<string>|string |
63 | | - */ |
64 | | - protected array|string $previousKeys = ''; |
65 | | - |
66 | 75 | /** |
67 | 76 | * Whether the cipher-text should be raw. If set to false, then it will be base64 encoded. |
68 | 77 | */ |
69 | 78 | protected bool $rawData = true; |
70 | 79 |
|
71 | | - /** |
72 | | - * Encryption key info. |
73 | | - * This setting is only used by OpenSSLHandler. |
74 | | - * |
75 | | - * Set to 'encryption' for CI3 Encryption compatibility. |
76 | | - */ |
77 | | - public string $encryptKeyInfo = ''; |
78 | | - |
79 | | - /** |
80 | | - * Authentication key info. |
81 | | - * This setting is only used by OpenSSLHandler. |
82 | | - * |
83 | | - * Set to 'authentication' for CI3 Encryption compatibility. |
84 | | - */ |
85 | | - public string $authKeyInfo = ''; |
86 | | - |
87 | 80 | /** |
88 | 81 | * {@inheritDoc} |
89 | 82 | */ |
@@ -125,90 +118,48 @@ public function encrypt(#[SensitiveParameter] $data, #[SensitiveParameter] $para |
125 | 118 | */ |
126 | 119 | public function decrypt($data, #[SensitiveParameter] $params = null) |
127 | 120 | { |
128 | | - // Allow key override |
129 | | - if ($params !== null) { |
130 | | - $this->key = is_array($params) && isset($params['key']) ? $params['key'] : $params; |
131 | | - } |
| 121 | + $decryptParams = $params ?? $this->key; |
132 | 122 |
|
133 | | - if (empty($this->key)) { |
| 123 | + if (empty($decryptParams)) { |
134 | 124 | throw EncryptionException::forNeedsStarterKey(); |
135 | 125 | } |
136 | 126 |
|
137 | | - // Only use fallback keys if no custom key was provided in params |
138 | | - $useFallback = ! isset($params['key']); |
139 | | - |
140 | | - $attemptDecrypt = function ($key) use ($data): array { |
141 | | - try { |
142 | | - $result = $this->decryptWithKey($data, $key); |
143 | | - |
144 | | - return ['success' => true, 'data' => $result]; |
145 | | - } catch (EncryptionException $e) { |
146 | | - return ['success' => false, 'exception' => $e]; |
147 | | - } |
148 | | - }; |
149 | | - |
150 | | - $result = $attemptDecrypt($this->key); |
| 127 | + return $this->tryDecryptWithFallback($data, $decryptParams, function ($data, $params): string { |
| 128 | + $key = is_array($params) && isset($params['key']) ? $params['key'] : $params; |
151 | 129 |
|
152 | | - if ($result['success']) { |
153 | | - return $result['data']; |
154 | | - } |
| 130 | + $authKey = \hash_hkdf($this->digest, $key, 0, $this->authKeyInfo); |
155 | 131 |
|
156 | | - $originalException = $result['exception']; |
| 132 | + $hmacLength = $this->rawData |
| 133 | + ? $this->digestSize[$this->digest] |
| 134 | + : $this->digestSize[$this->digest] * 2; |
157 | 135 |
|
158 | | - // If primary key failed and fallback is allowed, try previous keys |
159 | | - if ($useFallback && ! in_array($this->previousKeys, ['', '0', []], true)) { |
160 | | - foreach ($this->previousKeys as $previousKey) { |
161 | | - $fallbackResult = $attemptDecrypt($previousKey); |
| 136 | + $hmacKey = self::substr($data, 0, $hmacLength); |
| 137 | + $data = self::substr($data, $hmacLength); |
| 138 | + $hmacCalc = \hash_hmac($this->digest, $data, $authKey, $this->rawData); |
162 | 139 |
|
163 | | - if ($fallbackResult['success']) { |
164 | | - return $fallbackResult['data']; |
165 | | - } |
| 140 | + if (! hash_equals($hmacKey, $hmacCalc)) { |
| 141 | + throw EncryptionException::forAuthenticationFailed(); |
166 | 142 | } |
167 | | - } |
168 | 143 |
|
169 | | - // All attempts failed - throw the original exception |
170 | | - throw $originalException; |
171 | | - } |
| 144 | + $data = $this->rawData ? $data : base64_decode($data, true); |
172 | 145 |
|
173 | | - /** |
174 | | - * Decrypt the data with the provided key |
175 | | - * |
176 | | - * @param string $data |
177 | | - * @param string $key |
178 | | - * |
179 | | - * @return false|string |
180 | | - * |
181 | | - * @throws EncryptionException |
182 | | - */ |
183 | | - protected function decryptWithKey($data, #[SensitiveParameter] $key) |
184 | | - { |
185 | | - // derive a secret key |
186 | | - $authKey = \hash_hkdf($this->digest, $key, 0, $this->authKeyInfo); |
187 | | - |
188 | | - $hmacLength = $this->rawData |
189 | | - ? $this->digestSize[$this->digest] |
190 | | - : $this->digestSize[$this->digest] * 2; |
191 | | - |
192 | | - $hmacKey = self::substr($data, 0, $hmacLength); |
193 | | - $data = self::substr($data, $hmacLength); |
194 | | - $hmacCalc = \hash_hmac($this->digest, $data, $authKey, $this->rawData); |
| 146 | + if ($ivSize = \openssl_cipher_iv_length($this->cipher)) { |
| 147 | + $iv = self::substr($data, 0, $ivSize); |
| 148 | + $data = self::substr($data, $ivSize); |
| 149 | + } else { |
| 150 | + $iv = null; |
| 151 | + } |
195 | 152 |
|
196 | | - if (! hash_equals($hmacKey, $hmacCalc)) { |
197 | | - throw EncryptionException::forAuthenticationFailed(); |
198 | | - } |
| 153 | + // derive a secret key |
| 154 | + $encryptKey = \hash_hkdf($this->digest, $key, 0, $this->encryptKeyInfo); |
199 | 155 |
|
200 | | - $data = $this->rawData ? $data : base64_decode($data, true); |
| 156 | + $decryptedData = \openssl_decrypt($data, $this->cipher, $encryptKey, OPENSSL_RAW_DATA, $iv); |
201 | 157 |
|
202 | | - if ($ivSize = \openssl_cipher_iv_length($this->cipher)) { |
203 | | - $iv = self::substr($data, 0, $ivSize); |
204 | | - $data = self::substr($data, $ivSize); |
205 | | - } else { |
206 | | - $iv = null; |
207 | | - } |
208 | | - |
209 | | - // derive a secret key |
210 | | - $encryptKey = \hash_hkdf($this->digest, $key, 0, $this->encryptKeyInfo); |
| 158 | + if ($decryptedData === false) { |
| 159 | + throw EncryptionException::forDecryptionFailed(); |
| 160 | + } |
211 | 161 |
|
212 | | - return \openssl_decrypt($data, $this->cipher, $encryptKey, OPENSSL_RAW_DATA, $iv); |
| 162 | + return $decryptedData; |
| 163 | + }); |
213 | 164 | } |
214 | 165 | } |
0 commit comments