File: /var/www/html/wpwatermates_err/wp-content/plugins/defender-security/src/component/crypt.php
<?php
namespace WP_Defender\Component;
use WP_Defender\Traits\IO;
use WP_Error;
/**
* Class Crypt.
*
* @since 3.3.1
* @package WP_Defender\Component
*/
class Crypt extends \Calotes\Base\Component {
use IO;
/**
* Generates cryptographically secure pseudo-random bytes.
*
* @param int $bytes
*
* @return string
*/
public static function random_bytes( int $bytes ): string {
// Try with random_bytes.
if ( function_exists( 'random_bytes' ) ) {
try {
$rand = random_bytes( $bytes );
if ( is_string( $rand ) && strlen( $rand ) === $bytes ) {
return $rand;
}
} catch ( \Exception $e ) {
$_this = new self();
$_this->log( $e->getMessage(), 'internal.log' );
}
}
// Try with openssl_random_pseudo_bytes.
if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
$rand = openssl_random_pseudo_bytes( $bytes, $strong );
if ( is_string( $rand ) && strlen( $rand ) === $bytes ) {
return $rand;
}
}
// Not safe. Use in extreme cases.
$return = '';
for ( $i = 0; $i < $bytes; $i++ ) {
$return .= chr( mt_rand( 0, 255 ) );
}
return $return;
}
/**
* Generates cryptographically secure pseudo-random integers.
*
* @param int $min
* @param int $max
*
* @return int
*/
public static function random_int( $min = 0, $max = 0x7FFFFFFF ): int {
if ( function_exists( 'random_int' ) ) {
try {
return random_int( $min, $max );
} catch ( \Exception $e ) {
$_this = new self();
$_this->log( $e->getMessage(), 'internal.log' );
}
}
$diff = $max - $min;
$bytes = self::random_bytes( 4 );
if ( 4 !== strlen( $bytes ) ) {
throw new \RuntimeException( 'Unable to get 4 bytes' );
}
$val = unpack( 'nint', $bytes );
$val = $val['int'] & 0x7FFFFFFF;
// Convert to [0,1].
$fp = (float) $val / 2147483647.0;
return (int) ( round( $fp * $diff ) + $min );
}
/**
* Compare two strings to avoid timing attacks.
*
* @param string $expected
* @param string $actual
*
* @return bool
*/
public static function compare_lines( $expected, $actual ): bool {
if ( function_exists( 'hash_equals' ) ) {
return hash_equals( $expected, $actual );
}
$len_expected = mb_strlen( $expected, '8bit' );
$len_actual = mb_strlen( $actual, '8bit' );
$len = min( $len_expected, $len_actual );
$result = 0;
for ( $i = 0; $i < $len; $i++ ) {
$result |= ord( $expected[ $i ] ) ^ ord( $actual[ $i ] );
}
$result |= $len_expected ^ $len_actual;
return 0 === $result;
}
/**
* Encrypt data.
*
* @param string $value
* @param string $key
*
* @return string|WP_Error
* @throws \SodiumException
*/
private static function encrypt( $value, $key ) {
$key = base64_decode( $key );
if ( SODIUM_CRYPTO_SECRETBOX_KEYBYTES !== mb_strlen( $key, '8bit' ) ) {
return new WP_Error(
Error_Code::ENCRYPT_ERROR,
__( 'The issue with Sodium library.', 'defender-security' )
);
}
$nonce = self::random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
$ciphertext = sodium_crypto_secretbox( $value, $nonce, $key );
return base64_encode( $nonce . $ciphertext );
}
/**
* Decrypt encrypted data.
*
* @param string $encoded_value
* @param string $key
*
* @return string|WP_Error
* @throws \SodiumException
*/
private static function decrypt( $encoded_value, $key ) {
if ( ! $encoded_value || '' === $key ) {
return new WP_Error(
Error_Code::DECRYPT_ERROR,
__( 'Please re-setup 2FA TOTP method again.', 'defender-security' )
);
}
$key = base64_decode( $key );
$decoded = base64_decode( $encoded_value );
$nonce = mb_substr( $decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit' );
$ciphertext = mb_substr( $decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit' );
$decrypted = sodium_crypto_secretbox_open( $ciphertext, $nonce, $key );
if ( false === $decrypted) {
return new WP_Error(
Error_Code::DECRYPT_ERROR,
__( 'Please re-setup 2FA TOTP method again.', 'defender-security' )
);
}
return $decrypted;
}
/**
* Get the path to a file with a random key. This is used for 2FA TOTP.
*
* @return string
*/
public static function get_path_to_key_file() {
return wp_normalize_path( WP_CONTENT_DIR ) . DIRECTORY_SEPARATOR . 'wp-defender-secrets.php';
}
/**
* Get decrypted data.
*
* @param string $data
*
* @return string|WP_Error
* @throws \SodiumException
*/
public static function get_decrypted_data( $data ) {
$key = self::get_random_key();
if ( is_wp_error( $key ) ) {
return $key;
}
return self::decrypt( $data, $key );
}
/**
* Get encrypted data.
*
* @param string $data
*
* @return string|WP_Error
* @throws \SodiumException
*/
public static function get_encrypted_data( $data ) {
$key = self::get_random_key();
if ( is_wp_error( $key ) ) {
return $key;
}
return self::encrypt( $data, $key );
}
/**
* Get random key.
*
* @return string|WP_Error
* @throws \SodiumException
*/
private static function get_random_key() {
$file = self::get_path_to_key_file();
if ( ! file_exists( $file ) ) {
return new WP_Error(
Error_Code::IS_EMPTY,
__( 'The Defender file with the random key does not exist.', 'defender-security' )
);
}
if ( ! defined( 'WP_DEFENDER_TOTP_KEY' ) ) {
require_once $file;
}
if ( '{{__REPLACE_CODE__}}' !== constant( 'WP_DEFENDER_TOTP_KEY' ) ) {
return WP_DEFENDER_TOTP_KEY;
} else {
return new WP_Error( Error_Code::INVALID, __( 'The Defender file with the random key is incorrect.', 'defender-security' ) );
}
}
/**
* Generate a random key.
*
* @return string
*/
protected function generate_random_key(): string {
return base64_encode( sodium_crypto_secretbox_keygen() );
}
/**
* Create a file with a random key.
*
* @return bool
*/
public function create_key_file(): bool {
$to = self::get_path_to_key_file();
if ( ! file_exists( $to ) ) {
// Move a template file to WP_CONTENT and replace the file content.
$template_file = WP_DEFENDER_DIR . 'src' . DIRECTORY_SEPARATOR . 'component' . DIRECTORY_SEPARATOR
. 'wp-defender-sample.php';
if ( copy( $template_file, $to ) ) {
$content = file_get_contents( $to );
if ( false !== strpos( $content, '{{__REPLACE_CODE__}}' ) ) {
$new_content = str_replace( '{{__REPLACE_CODE__}}', $this->generate_random_key(), $content );
return (bool) file_put_contents( $to, $new_content, LOCK_EX );
}
}
// The file was not copied.
return false;
}
// Everything is fine. The file exists.
return true;
}
}