PluginProbe
Auto Alt Text / trunk
Auto Alt Text vtrunk
3.0.3 2.8.2 1.3.1 1.3.2 2.0.0 2.1.0 2.1.1 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.7.0 2.8.0 2.8.1 All 28 releases
← All changes | src/App/Utilities/Encryption.php +75 -18 2.3.4trunk View file →
@@ -1,8 +1,9 @@
1 1 <?php
2 2
3 3 namespace AATXT\App\Utilities;
4 4
5 +use AATXT\Config\Constants;
5 6 use RuntimeException;
6 7
7 8 final class Encryption
8 9 {
@@ -48,10 +49,30 @@
48 49 return base64_encode($iv . $raw_value);
49 50 }
50 51
51 52 /**
53 + * Attempts to decrypt a raw value using the provided key and salt.
54 + **/
55 + private function attemptDecrypt(string $rawValue, string $key, string $salt): string
56 + {
57 + $decoded = base64_decode($rawValue, true);
58 + $method = 'aes-256-ctr';
59 + $ivLength = openssl_cipher_iv_length($method);
60 + $iv = substr($decoded, 0, $ivLength);
61 + $cipher = substr($decoded, $ivLength);
62 + $decrypted = openssl_decrypt($cipher, $method, $key, 0, $iv);
63 +
64 + // If decryption fails or the salt is not present, return an empty string
65 + if (! $decrypted || substr($decrypted, -strlen($salt)) !== $salt) {
66 + return '';
67 + }
68 + return substr($decrypted, 0, -strlen($salt));
69 + }
70 +
71 + /**
72 + * Decrypts a raw value trying with the plugin key and salt first, then falling back to the logged-in key and salt.
52 73 * @param string $rawValue
53 - * @return string|bool
74 + * @return string
54 75 */
55 76 public function decrypt(string $rawValue): string
56 77 {
57 78 if (empty($rawValue) || ! extension_loaded('openssl')) {
@@ -58,32 +79,33 @@
58 79 return '';
59 80 }
60 81
61 82 try {
62 - $decoded = base64_decode($rawValue, true);
63 - $method = 'aes-256-ctr';
64 - $ivLength = openssl_cipher_iv_length($method);
65 - $iv = substr($decoded, 0, $ivLength);
66 - $cipherText = substr($decoded, $ivLength);
67 - $decrypted = openssl_decrypt($cipherText, $method, $this->key, 0, $iv);
83 + $plain = $this->attemptDecrypt($rawValue, $this->key, $this->salt);
84 + if ($plain !== '') {
85 + return $plain;
86 + }
68 87
69 - // If decryption fails or the salt is not present, return an empty string
70 - if ( ! $decrypted || substr($decrypted, -strlen($this->salt)) !== $this->salt) {
71 - return '';
88 + // If the decryption with the plugin key fails, try as fallback with the logged-in key and salt
89 + if (defined('LOGGED_IN_KEY') && defined('LOGGED_IN_SALT')) {
90 + $plainOld = $this->attemptDecrypt($rawValue, LOGGED_IN_KEY, LOGGED_IN_SALT);
91 + if ($plainOld !== '') {
92 + return $plainOld;
93 + }
72 94 }
73 95
74 - return substr($decrypted, 0, -strlen($this->salt));
96 + return '';
75 97 } catch (\Throwable $e) {
76 98 throw new RuntimeException('Decryption failed.');
77 99 }
78 100 }
79 101
80 -
81 - /**
82 - * Get key from WordPress Authentication Unique Keys and Salts
83 - */
84 102 private function getKey(): string
85 103 {
104 + if ( defined( 'AATXT_ENCRYPTION_KEY' ) && '' !== AATXT_ENCRYPTION_KEY ) {
105 + return AATXT_ENCRYPTION_KEY;
106 + }
107 + // If the constant is not defined, use the WordPress constants LOGGED_IN_KEY and LOGGED_IN_SALT
86 108 if (defined('LOGGED_IN_KEY') && '' !== LOGGED_IN_KEY) {
87 109 return LOGGED_IN_KEY;
88 110 }
89 111
@@ -90,13 +112,14 @@
90 112 // If this is reached, you're either not on a live site or have a serious security issue.
91 113 return 'warning-not-logged-in-key-constant-defined';
92 114 }
93 115
94 - /**
95 - * Get salt from WordPress Authentication Unique Keys and Salts
96 - */
97 116 public function getSalt(): string
98 117 {
118 + if ( defined( 'AATXT_ENCRYPTION_SALT' ) && '' !== AATXT_ENCRYPTION_SALT ) {
119 + return AATXT_ENCRYPTION_SALT;
120 + }
121 + // If the constant is not defined, use the WordPress constants LOGGED_IN_KEY and LOGGED_IN_SALT
99 122 if (defined('LOGGED_IN_SALT') && '' !== LOGGED_IN_SALT) {
100 123 return LOGGED_IN_SALT;
101 124 }
102 125
@@ -101,6 +124,40 @@
101 124 }
102 125
103 126 // If this is reached, you're either not on a live site or have a serious security issue.
104 127 return 'warning-not-logged-in-salt-constant-defined';
128 + }
129 +
130 + /**
131 + * Migrate legacy API keys from the old encryption method to the new one.
132 + * This is necessary for backward compatibility with older versions of the plugin.
133 + */
134 + public function migrateLegacyApiKeys(): void
135 + {
136 + $fields = [
137 + Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI,
138 + Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION,
139 + Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE,
140 + ];
141 +
142 + foreach ($fields as $optionName) {
143 + $raw = get_option($optionName);
144 + if (empty($raw)) {
145 + continue;
146 + }
147 +
148 + if (! defined('LOGGED_IN_KEY') || ! defined('LOGGED_IN_SALT')) {
149 + continue;
150 + }
151 + // Try to decrypt options using the old method with the WordPress constants LOGGED_IN_KEY and LOGGED_IN_SALT
152 + $plain = $this->attemptDecrypt($raw, LOGGED_IN_KEY, LOGGED_IN_SALT);
153 +
154 + if ($plain === '') {
155 + // Decryption failed, cause the option is encrypted with the new method or the old method failed
156 + continue;
157 + }
158 +
159 + //Resave the option as plain because the action "encryptDataOnUpdate" will encrypt before saving in the database
160 + update_option($optionName, $plain);
161 + }
105 162 }
106 163 }