for further details. * * @return string A unique, unguessable, non-numeric, caSe-insensitive key (20 chars max). */ public static function uunnci_key_20_max() { $microtime_19_max = number_format(microtime(true), 9, '.', ''); // e.g., `9999999999`.`999999999` (max decimals: `9`, max overall precision: `19`). // Assuming timestamp is never > 10 digits; i.e., before `Sat, 20 Nov 2286 17:46:39 GMT`. list($seconds_10_max, $microseconds_9_max) = explode('.', $microtime_19_max, 2); // e.g., `array(`9999999999`, `999999999`)`. Max total digits combined: `19`. $seconds_base36 = base_convert($seconds_10_max, '10', '36'); // e.g., max `9999999999`, to base 36. $microseconds_base36 = base_convert($microseconds_9_max, '10', '36'); // e.g., max `999999999`, to base 36. $mt_rand_base36 = base_convert(mt_rand(1, 999999999), '10', '36'); // e.g., max `999999999`, to base 36. $key = 'k'.$mt_rand_base36.$seconds_base36.$microseconds_base36; // e.g., `kgjdgxr4ldqpdrgjdgxr`. return $key; // Max possible value: `kgjdgxr4ldqpdrgjdgxr` (20 chars). } /** * Encrypt w/ best possible technique. * * @since 3.5 Nearly the first release. * * @param string $string String to encrypt. * @param string $key Optional custom encryption key. * @param bool $w_md5_cs Defaults to true. When true, an MD5 checksum. * @param bool|null $allow_defuse Allow Defuse encryption as a better alternative? * * @return string Encrypted string. */ public static function encrypt($string = '', $key = '', $w_md5_cs = true, $allow_defuse = null) { $allow_defuse = isset($allow_defuse) ? $allow_defuse : apply_filters('c_ws_plugin__s2member_allow_defuse', true); if ($allow_defuse && version_compare(PHP_VERSION, '7.0.4', '>=')) { return c_ws_plugin__s2member_utils_defuse::encrypt($string, $key); } // This is a new/improved way of handling encryption. if (function_exists('mcrypt_encrypt') && in_array('rijndael-256', @mcrypt_list_algorithms()) && in_array('cbc', @mcrypt_list_modes())) { // $string = is_string($string) ? $string : ''; $string = isset($string[0]) ? '~r2|'.$string : ''; $key = self::key($key); // Get encryption key. $key = substr($key, 0, @mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC)); $iv = c_ws_plugin__s2member_utils_strings::random_str_gen(@mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), false); if (isset($string[0]) && is_string($e = @mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC, $iv)) && isset($e[0])) { $e = '~r2:'.$iv.($w_md5_cs ? ':'.md5($e) : '').'|'.$e; } return isset($e) && is_string($e) && isset($e[0]) ? ($base64 = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode($e)) : ''; // Default to empty string. } return self::xencrypt($string, $key, $w_md5_cs); } /** * Decrypt w/ best possible technique. * * @since 3.5 Nearly the first release. * @since 260810 Allows Defuse decryption on PHP 5.6+. * * @param string $base64 String to decrypt (base64). * @param string $key Optional custom decryption key. * @param bool|null $allow_defuse Allow Defuse decryption as a better alternative? * * @return string Decrypted string, else empty string. */ public static function decrypt($base64 = '', $key = '', $allow_defuse = null) { if (!is_string($base64) || !isset($base64[0])) { return ''; // Not possible. } // Fail when not a string or empty. $allow_defuse = isset($allow_defuse) ? $allow_defuse : apply_filters('c_ws_plugin__s2member_allow_defuse', true); //260810 Allow PHP 5.6 to read Defuse ciphertext created on newer PHP versions; decryption was previously gated to PHP 7.0.4+. // Defuse encryption remains gated separately so PHP 5.6 does not generate new Defuse ciphertext yet. if ($allow_defuse && ($_d = c_ws_plugin__s2member_utils_defuse::decrypt($base64, $key))) { return $string = $_d; // Defuse success. } if (function_exists('mcrypt_decrypt') && in_array('rijndael-256', @mcrypt_list_algorithms()) && in_array('cbc', @mcrypt_list_modes())) { // $e = c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64); if (preg_match('/^~r2\:([a-zA-Z0-9]+)(?:\:([a-zA-Z0-9]+))?\|(.*)$/s', $e, $iv_md5_e)) { $key = self::key($key); // Get encryption key. $key = substr($key, 0, @mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC)); if (isset($iv_md5_e[3][0]) && (empty($iv_md5_e[2]) || $iv_md5_e[2] === md5($iv_md5_e[3]))) { $d = @mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $iv_md5_e[3], MCRYPT_MODE_CBC, $iv_md5_e[1]); } if (!isset($d)) { // Failed above? return ''; // Empty string on failure. } elseif (!strlen($d = preg_replace('/^~r2\|/', '', $d, 1, $r2)) || !$r2) { return ''; // Empty string on failure. } return $string = rtrim($d, "\0\4"); } } return self::xdecrypt($base64, $key); } /** * XOR two-way encryption/decryption, with a base64 wrapper. * * @since 3.5 Nearly the first release. * * @param string $string A string of data to encrypt. * @param string $key Optional. Key used for encryption. Defaults to the one configured for s2Member. Short of that, defaults to: ``wp_salt()``. * @param bool $w_md5_cs Optional. Defaults to true. When true, an MD5 checksum is used in the encrypted string *(recommended)*. * * @return string Encrypted string. */ public static function xencrypt($string = '', $key = '', $w_md5_cs = true) { $string = is_string($string) ? $string : ''; $string = isset($string[0]) ? '~xe|'.$string : ''; $key = self::key($key); // Get encryption key. for ($i = 1, $e = ''; $i <= strlen($string); ++$i) { $char = substr($string, $i - 1, 1); $keychar = substr($key, ($i % strlen($key)) - 1, 1); //260830.2134 PHP 8.5 deprecates chr() values outside 0..255; mask explicitly to preserve chr()'s historical byte-wrap behavior. $e .= chr((ord($char) + ord($keychar)) & 0xFF); } $e = isset($e[0]) ? '~xe'.($w_md5_cs ? ':'.md5($e) : '').'|'.$e : ''; return $base64 = isset($e[0]) ? ($base64 = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode($e)) : ''; } /** * XOR decryption. * * @since 3.5 Nearly the first release. * * @param string $base64 String to decrypt (base64). * @param string $key Optional custom decryption key. * * @return string Decrypted string. */ public static function xdecrypt($base64 = '', $key = '') { if (!is_string($base64) || !isset($base64[0])) { return ''; // Not possible. } // Fail when not a string or empty. $e = c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64); if (preg_match('/^~xe(?:\:([a-zA-Z0-9]+))?\|(.*)$/s', $e, $md5_e)) { $key = self::key($key); // Get encryption key. if (isset($md5_e[2][0]) && (empty($md5_e[1]) || $md5_e[1] === md5($md5_e[2]))) { for ($i = 1, $d = ''; $i <= strlen($md5_e[2]); ++$i) { $char = substr($md5_e[2], $i - 1, 1); $keychar = substr($key, ($i % strlen($key)) - 1, 1); //260830.2134 PHP 8.5 deprecates chr() values outside 0..255; mask explicitly to preserve chr()'s historical byte-wrap behavior. $d .= chr((ord($char) - ord($keychar)) & 0xFF); } // Reverse XOR encryption. } // Else the checksum was not a match. if (!isset($d)) { // Failed above? return ''; // Empty string on failure. } elseif (!strlen($d = preg_replace('/^~xe\|/', '', $d, 1, $xe)) || !$xe) { return ''; // Empty string on failure. } return $string = $d; // Decryption success. } return ''; // Empty string on failure. } } }