PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.2
WP 2FA – Two-factor authentication for WordPress v2.9.2
1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Authenticator / class-open-ssl.php
wp-2fa / includes / classes / Authenticator Last commit date
assets 10 months ago class-authentication.php 10 months ago class-login.php 10 months ago class-open-ssl.php 10 months ago class-reset-password.php 10 months ago index.php 10 months ago
class-open-ssl.php
214 lines
1 <?php
2 /**
3 * Open SSL encrypt / decrypt class.
4 *
5 * @package wp2fa
6 * @copyright 2025 Melapress
7 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
8 * @link https://wordpress.org/plugins/wp-2fa/
9 * @since 2.0.0
10 */
11
12 declare(strict_types=1);
13
14 namespace WP2FA\Authenticator;
15
16 use WP2FA\WP2FA;
17
18 use function WP2FA\Core\wp_salt;
19
20 /**
21 * Open_SSL - Class for encryption and decryption of the string using open_ssl method
22 *
23 * @since 2.0.0
24 */
25 if ( ! class_exists( '\WP2FA\Authenticator\Open_SSL' ) ) {
26
27 /**
28 * Responsible for SSL operations
29 */
30 class Open_SSL {
31
32 const CIPHER_METHOD = 'aes-256-ctr';
33 const BLOCK_BYTE_SIZE = 16;
34 const DIGEST_ALGORITHM = 'SHA256';
35 const SECRET_KEY_PREFIX = 'lsc_';
36
37 /**
38 * Internal cache var for the PHP ssl functions availability
39 *
40 * @var mixed|boolean
41 *
42 * @since 2.0.0
43 */
44 private static $ssl_enabled = null;
45
46 /**
47 * Encrypts given text
48 *
49 * @param string $text - Text to be encrypted.
50 *
51 * @return string
52 *
53 * @throws \RuntimeException - If encryption fails.
54 *
55 * @since 2.0.0
56 */
57 public static function encrypt( string $text ): string {
58 if ( self::is_ssl_available() ) {
59 $iv = self::secure_random( self::BLOCK_BYTE_SIZE );
60 $key = \openssl_digest( \base64_decode( wp_salt() ), self::DIGEST_ALGORITHM, true );
61 $encrypted_text = \openssl_encrypt(
62 $text,
63 self::CIPHER_METHOD,
64 $key,
65 OPENSSL_RAW_DATA,
66 $iv
67 );
68
69 if ( false === $encrypted_text ) {
70 throw new \RuntimeException( 'Encryption failed.' );
71 }
72
73 $text = \base64_encode( $iv . $encrypted_text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
74 }
75
76 return $text;
77 }
78
79 /**
80 * Decrypts crypt text
81 *
82 * @param string $text - Encrypted text to be decrypted.
83 *
84 * @return string
85 *
86 * @throws \RuntimeException - If decryption fails.
87 *
88 * @since 2.0.0
89 */
90 public static function decrypt( string $text ): string {
91 if ( self::is_ssl_available() ) {
92 $decoded_base = \base64_decode( $text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
93
94 $key = \openssl_digest( \base64_decode( wp_salt() ), self::DIGEST_ALGORITHM, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
95
96 $ivlen = \openssl_cipher_iv_length( self::CIPHER_METHOD );
97
98 $iv = \substr( $decoded_base, 0, $ivlen );
99 $ciphertext_raw = \substr( $decoded_base, $ivlen );
100 $decrypted_text = \openssl_decrypt( $ciphertext_raw, self::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv );
101
102 if ( false === $decrypted_text ) {
103 throw new \RuntimeException( 'Decryption failed.' );
104 }
105
106 $text = $decrypted_text;
107 }
108
109 return $text;
110 }
111
112 /**
113 * Decrypts crypt text
114 *
115 * @param string $text - Encrypted text to be decrypted.
116 *
117 * @return string
118 *
119 * @throws \RuntimeException - If legacy decryption fails.
120 *
121 * @since 2.0.0
122 */
123 public static function decrypt_legacy( string $text ): string {
124 if ( self::is_ssl_available() ) {
125 $decoded_base = \base64_decode( $text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
126
127 $key = \openssl_digest( \base64_decode( WP2FA::get_secret_key() ), self::DIGEST_ALGORITHM, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
128
129 $ivlen = \openssl_cipher_iv_length( self::CIPHER_METHOD );
130
131 $iv = \substr( $decoded_base, 0, $ivlen );
132 $ciphertext_raw = \substr( $decoded_base, $ivlen );
133 $decrypted_text = \openssl_decrypt( $ciphertext_raw, self::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv );
134
135 if ( false === $decrypted_text ) {
136 throw new \RuntimeException( 'Decryption failed.' );
137 }
138
139 $text = $decrypted_text;
140 }
141
142 return $text;
143 }
144
145 /**
146 * Decrypts old wps_ secret strings
147 *
148 * @param string $text - The encrypted string.
149 *
150 * @return string
151 *
152 * @throws \RuntimeException - If decryption fails.
153 *
154 * @since 2.3.0
155 */
156 public static function decrypt_wps( string $text ): string {
157 if ( self::is_ssl_available() ) {
158 $decoded_base = \base64_decode( $text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
159
160 $key = \openssl_digest( \base64_decode( \wp_salt() ), self::DIGEST_ALGORITHM, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
161
162 $ivlen = \openssl_cipher_iv_length( self::CIPHER_METHOD );
163
164 $iv = \substr( $decoded_base, 0, $ivlen );
165 $ciphertext_raw = \substr( $decoded_base, $ivlen );
166 $decrypted_text = \openssl_decrypt( $ciphertext_raw, self::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv );
167
168 if ( false === $decrypted_text ) {
169 throw new \RuntimeException( 'Decryption failed.' );
170 }
171
172 $text = $decrypted_text;
173 }
174
175 return $text;
176 }
177
178 /**
179 * Generates random bytes by given size
180 *
181 * @param integer $octets - Number of octets for use for random generator.
182 *
183 * @return string
184 *
185 * @since 2.0.0
186 */
187 public static function secure_random( int $octets = 0 ): string {
188 if ( 0 === $octets ) {
189 $octets = self::BLOCK_BYTE_SIZE;
190 }
191
192 return \random_bytes( $octets );
193 }
194
195 /**
196 * Checks the open ssl methods existence
197 *
198 * @return boolean
199 *
200 * @since 2.0.0
201 */
202 public static function is_ssl_available(): bool {
203 if ( null === self::$ssl_enabled ) {
204 self::$ssl_enabled = false;
205 if ( \function_exists( 'openssl_encrypt' ) ) {
206 self::$ssl_enabled = true;
207 }
208 }
209
210 return self::$ssl_enabled;
211 }
212 }
213 }
214