PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260805
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260805
260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 All 189 releases
s2member / src / includes / classes / utils-encryption.inc.php

utils-encryption.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions 260805, at src/includes/classes/utils-encryption.inc.php

232 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreFile
3 /**
4 * Encryption utilities.
5 *
6 * @since 3.5 Nearly the first release.
7 */
8 if (!defined('WPINC')) { // MUST have.
9 exit('Do not access this file directly.');
10 }
11 if (!class_exists('c_ws_plugin__s2member_utils_encryption')) {
12 /**
13 * Encryption utilities.
14 *
15 * @since 3.5 Nearly the first release.
16 */
17 class c_ws_plugin__s2member_utils_encryption
18 {
19 /**
20 * Encryption key.
21 *
22 * @since 111106 Get key.
23 *
24 * @param string $key Custom key.
25 *
26 * @return string Encryption key.
27 */
28 public static function key($key = '')
29 {
30 if (($key = trim((string) $key))) {
31 return $key;
32 } elseif (($key = $GLOBALS['WS_PLUGIN__']['s2member']['o']['sec_encryption_key'])) {
33 return $key;
34 } elseif (($key = wp_salt())) {
35 return $key;
36 }
37 return $key = md5($_SERVER['HTTP_HOST']);
38 }
39
40 /**
41 * A unique, unguessable, non-numeric, caSe-insensitive key (20 chars max).
42 *
43 * @since 150124 Adding gift code generation.
44 *
45 * @note 32-bit systems usually have `PHP_INT_MAX` = `2147483647`.
46 * We limit `mt_rand()` to a max of `999999999`.
47 *
48 * @note A max possible length of 20 chars assumes this function
49 * will not be called after `Sat, 20 Nov 2286 17:46:39 GMT`.
50 * At which point a UNIX timestamp will grow in size.
51 *
52 * @note Key always begins with a `k` to prevent PHP's `is_numeric()`
53 * function from ever thinking it's a number in a different representation.
54 * See: <http://php.net/manual/en/function.is-numeric.php> for further details.
55 *
56 * @return string A unique, unguessable, non-numeric, caSe-insensitive key (20 chars max).
57 */
58 public static function uunnci_key_20_max()
59 {
60 $microtime_19_max = number_format(microtime(true), 9, '.', '');
61 // e.g., `9999999999`.`999999999` (max decimals: `9`, max overall precision: `19`).
62 // Assuming timestamp is never > 10 digits; i.e., before `Sat, 20 Nov 2286 17:46:39 GMT`.
63
64 list($seconds_10_max, $microseconds_9_max) = explode('.', $microtime_19_max, 2);
65 // e.g., `array(`9999999999`, `999999999`)`. Max total digits combined: `19`.
66
67 $seconds_base36 = base_convert($seconds_10_max, '10', '36'); // e.g., max `9999999999`, to base 36.
68 $microseconds_base36 = base_convert($microseconds_9_max, '10', '36'); // e.g., max `999999999`, to base 36.
69 $mt_rand_base36 = base_convert(mt_rand(1, 999999999), '10', '36'); // e.g., max `999999999`, to base 36.
70 $key = 'k'.$mt_rand_base36.$seconds_base36.$microseconds_base36; // e.g., `kgjdgxr4ldqpdrgjdgxr`.
71
72 return $key; // Max possible value: `kgjdgxr4ldqpdrgjdgxr` (20 chars).
73 }
74
75 /**
76 * Encrypt w/ best possible technique.
77 *
78 * @since 3.5 Nearly the first release.
79 *
80 * @param string $string String to encrypt.
81 * @param string $key Optional custom encryption key.
82 * @param bool $w_md5_cs Defaults to true. When true, an MD5 checksum.
83 * @param bool|null $allow_defuse Allow Defuse encryption as a better alternative?
84 *
85 * @return string Encrypted string.
86 */
87 public static function encrypt($string = '', $key = '', $w_md5_cs = true, $allow_defuse = null)
88 {
89 $allow_defuse = isset($allow_defuse) ? $allow_defuse
90 : apply_filters('c_ws_plugin__s2member_allow_defuse', true);
91
92 if ($allow_defuse && version_compare(PHP_VERSION, '7.0.4', '>=')) {
93 return c_ws_plugin__s2member_utils_defuse::encrypt($string, $key);
94 } // This is a new/improved way of handling encryption.
95
96 if (function_exists('mcrypt_encrypt')
97 && in_array('rijndael-256', @mcrypt_list_algorithms())
98 && in_array('cbc', @mcrypt_list_modes())) {
99 //
100 $string = is_string($string) ? $string : '';
101 $string = isset($string[0]) ? '~r2|'.$string : '';
102
103 $key = self::key($key); // Get encryption key.
104 $key = substr($key, 0, @mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
105 $iv = c_ws_plugin__s2member_utils_strings::random_str_gen(@mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), false);
106
107 if (isset($string[0]) && is_string($e = @mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC, $iv)) && isset($e[0])) {
108 $e = '~r2:'.$iv.($w_md5_cs ? ':'.md5($e) : '').'|'.$e;
109 }
110 return isset($e) && is_string($e) && isset($e[0])
111 ? ($base64 = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode($e))
112 : ''; // Default to empty string.
113 }
114 return self::xencrypt($string, $key, $w_md5_cs);
115 }
116
117 /**
118 * Decrypt w/ best possible technique.
119 *
120 * @since 3.5 Nearly the first release.
121 *
122 * @param string $base64 String to decrypt (base64).
123 * @param string $key Optional custom decryption key.
124 * @param bool|null $allow_defuse Allow Defuse encryption as a better alternative?
125 *
126 * @return string Decrypted string, else empty string.
127 */
128 public static function decrypt($base64 = '', $key = '', $allow_defuse = null)
129 {
130 if (!is_string($base64) || !isset($base64[0])) {
131 return ''; // Not possible.
132 } // Fail when not a string or empty.
133
134 $allow_defuse = isset($allow_defuse) ? $allow_defuse
135 : apply_filters('c_ws_plugin__s2member_allow_defuse', true);
136
137 if ($allow_defuse && version_compare(PHP_VERSION, '7.0.4', '>=')
138 && ($_d = c_ws_plugin__s2member_utils_defuse::decrypt($base64, $key))) {
139 return $string = $_d; // Defuse success.
140 } // This is a new/improved way of handling decryption.
141
142 if (function_exists('mcrypt_decrypt')
143 && in_array('rijndael-256', @mcrypt_list_algorithms())
144 && in_array('cbc', @mcrypt_list_modes())) {
145 //
146 $e = c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64);
147
148 if (preg_match('/^~r2\:([a-zA-Z0-9]+)(?:\:([a-zA-Z0-9]+))?\|(.*)$/s', $e, $iv_md5_e)) {
149 $key = self::key($key); // Get encryption key.
150 $key = substr($key, 0, @mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
151
152 if (isset($iv_md5_e[3][0]) && (empty($iv_md5_e[2]) || $iv_md5_e[2] === md5($iv_md5_e[3]))) {
153 $d = @mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $iv_md5_e[3], MCRYPT_MODE_CBC, $iv_md5_e[1]);
154 }
155 if (!isset($d)) { // Failed above?
156 return ''; // Empty string on failure.
157 } elseif (!strlen($d = preg_replace('/^~r2\|/', '', $d, 1, $r2)) || !$r2) {
158 return ''; // Empty string on failure.
159 }
160 return $string = rtrim($d, "\0\4");
161 }
162 }
163 return self::xdecrypt($base64, $key);
164 }
165
166 /**
167 * XOR two-way encryption/decryption, with a base64 wrapper.
168 *
169 * @since 3.5 Nearly the first release.
170 *
171 * @param string $string A string of data to encrypt.
172 * @param string $key Optional. Key used for encryption. Defaults to the one configured for s2Member. Short of that, defaults to: ``wp_salt()``.
173 * @param bool $w_md5_cs Optional. Defaults to true. When true, an MD5 checksum is used in the encrypted string *(recommended)*.
174 *
175 * @return string Encrypted string.
176 */
177 public static function xencrypt($string = '', $key = '', $w_md5_cs = true)
178 {
179 $string = is_string($string) ? $string : '';
180 $string = isset($string[0]) ? '~xe|'.$string : '';
181 $key = self::key($key); // Get encryption key.
182
183 for ($i = 1, $e = ''; $i <= strlen($string); ++$i) {
184 $char = substr($string, $i - 1, 1);
185 $keychar = substr($key, ($i % strlen($key)) - 1, 1);
186 $e .= chr(ord($char) + ord($keychar));
187 }
188 $e = isset($e[0]) ? '~xe'.($w_md5_cs ? ':'.md5($e) : '').'|'.$e : '';
189 return $base64 = isset($e[0]) ? ($base64 = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode($e)) : '';
190 }
191
192 /**
193 * XOR decryption.
194 *
195 * @since 3.5 Nearly the first release.
196 *
197 * @param string $base64 String to decrypt (base64).
198 * @param string $key Optional custom decryption key.
199 *
200 * @return string Decrypted string.
201 */
202 public static function xdecrypt($base64 = '', $key = '')
203 {
204 if (!is_string($base64) || !isset($base64[0])) {
205 return ''; // Not possible.
206 } // Fail when not a string or empty.
207
208 $e = c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64);
209
210 if (preg_match('/^~xe(?:\:([a-zA-Z0-9]+))?\|(.*)$/s', $e, $md5_e)) {
211 $key = self::key($key); // Get encryption key.
212
213 if (isset($md5_e[2][0]) && (empty($md5_e[1]) || $md5_e[1] === md5($md5_e[2]))) {
214 for ($i = 1, $d = ''; $i <= strlen($md5_e[2]); ++$i) {
215 $char = substr($md5_e[2], $i - 1, 1);
216 $keychar = substr($key, ($i % strlen($key)) - 1, 1);
217 $d .= chr(ord($char) - ord($keychar));
218 } // Reverse XOR encryption.
219 } // Else the checksum was not a match.
220
221 if (!isset($d)) { // Failed above?
222 return ''; // Empty string on failure.
223 } elseif (!strlen($d = preg_replace('/^~xe\|/', '', $d, 1, $xe)) || !$xe) {
224 return ''; // Empty string on failure.
225 }
226 return $string = $d; // Decryption success.
227 }
228 return ''; // Empty string on failure.
229 }
230 }
231 }
232