Skip to content

Commit 486bf57

Browse files
committed
feat(encryption): Implement the main logic of using previous keys if decryption failed
1 parent 972ad6e commit 486bf57

File tree

2 files changed

+95
-6
lines changed

2 files changed

+95
-6
lines changed

system/Encryption/Handlers/OpenSSLHandler.php

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace CodeIgniter\Encryption\Handlers;
1515

1616
use CodeIgniter\Encryption\Exceptions\EncryptionException;
17-
use SensitiveParameter;
1817

1918
/**
2019
* Encryption handling for OpenSSL library
@@ -56,6 +55,20 @@ class OpenSSLHandler extends BaseHandler
5655
*/
5756
protected $key = '';
5857

58+
/**
59+
* Whether to fall back to previous keys when decryption fails.
60+
*
61+
* @var bool
62+
*/
63+
protected bool $previousKeysFallbackEnabled = false;
64+
65+
/**
66+
* List of previous keys for fallback decryption.
67+
*
68+
* @var string[]
69+
*/
70+
protected array $previousKeys = [];
71+
5972
/**
6073
* Whether the cipher-text should be raw. If set to false, then it will be base64 encoded.
6174
*/
@@ -127,8 +140,46 @@ public function decrypt($data, #[SensitiveParameter] $params = null)
127140
throw EncryptionException::forNeedsStarterKey();
128141
}
129142

143+
try {
144+
$result = $this->decryptWithKey($data, $this->key);
145+
} catch (EncryptionException $e) {
146+
$result = false;
147+
$exception = $e;
148+
}
149+
150+
if ($result === false && $this->previousKeysFallbackEnabled && ! empty($this->previousKeys)) {
151+
foreach ($this->previousKeys as $previousKey) {
152+
try {
153+
$result = $this->decryptWithKey($data, $previousKey);
154+
if ($result !== false) {
155+
return $result;
156+
}
157+
} catch (EncryptionException) {
158+
// Try next key
159+
}
160+
}
161+
}
162+
163+
if (isset($exception)) {
164+
throw $exception;
165+
}
166+
167+
return $result;
168+
}
169+
170+
/**
171+
* Decrypt the data with the provided key
172+
*
173+
* @param string $data
174+
* @param string $key
175+
*
176+
* @return false|string
177+
* @throws EncryptionException
178+
*/
179+
protected function decryptWithKey($data, #[SensitiveParameter] $key)
180+
{
130181
// derive a secret key
131-
$authKey = \hash_hkdf($this->digest, $this->key, 0, $this->authKeyInfo);
182+
$authKey = \hash_hkdf($this->digest, $key, 0, $this->authKeyInfo);
132183

133184
$hmacLength = $this->rawData
134185
? $this->digestSize[$this->digest]
@@ -152,7 +203,7 @@ public function decrypt($data, #[SensitiveParameter] $params = null)
152203
}
153204

154205
// derive a secret key
155-
$encryptKey = \hash_hkdf($this->digest, $this->key, 0, $this->encryptKeyInfo);
206+
$encryptKey = \hash_hkdf($this->digest, $key, 0, $this->encryptKeyInfo);
156207

157208
return \openssl_decrypt($data, $this->cipher, $encryptKey, OPENSSL_RAW_DATA, $iv);
158209
}

system/Encryption/Handlers/SodiumHandler.php

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace CodeIgniter\Encryption\Handlers;
1515

1616
use CodeIgniter\Encryption\Exceptions\EncryptionException;
17-
use SensitiveParameter;
1817

1918
/**
2019
* SodiumHandler uses libsodium in encryption.
@@ -80,6 +79,46 @@ public function decrypt($data, #[SensitiveParameter] $params = null)
8079
throw EncryptionException::forNeedsStarterKey();
8180
}
8281

82+
try {
83+
$result = $this->decryptWithKey($data, $this->key);
84+
sodium_memzero($this->key);
85+
} catch (EncryptionException $e) {
86+
$result = false;
87+
$exception = $e;
88+
sodium_memzero($this->key);
89+
}
90+
91+
if ($result === false && $this->previousKeysFallbackEnabled && ! empty($this->previousKeys)) {
92+
foreach ($this->previousKeys as $previousKey) {
93+
try {
94+
$result = $this->decryptWithKey($data, $previousKey);
95+
if (isset($result)) {
96+
return $result;
97+
}
98+
} catch (EncryptionException) {
99+
// Try next key
100+
}
101+
}
102+
}
103+
104+
if (isset($exception)) {
105+
throw $exception;
106+
}
107+
108+
return $result;
109+
}
110+
111+
/**
112+
* Decrypt the data with the provided key
113+
*
114+
* @param string $data
115+
* @param string $key
116+
*
117+
* @return string
118+
* @throws EncryptionException
119+
*/
120+
protected function decryptWithKey($data, #[SensitiveParameter] $key)
121+
{
83122
if (mb_strlen($data, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
84123
// message was truncated
85124
throw EncryptionException::forAuthenticationFailed();
@@ -90,7 +129,7 @@ public function decrypt($data, #[SensitiveParameter] $params = null)
90129
$ciphertext = self::substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
91130

92131
// decrypt data
93-
$data = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key);
132+
$data = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
94133

95134
if ($data === false) {
96135
// message was tampered in transit
@@ -106,7 +145,6 @@ public function decrypt($data, #[SensitiveParameter] $params = null)
106145

107146
// cleanup buffers
108147
sodium_memzero($ciphertext);
109-
sodium_memzero($this->key);
110148

111149
return $data;
112150
}

0 commit comments

Comments
 (0)