Because cats have 9 lives, but servers don't - so they need backup-restore! Complete backup solution with S3/MinIO support. - Full WordPress backup (files + database) - S3 / MinIO / S3-compatible storage backends - Scheduled automatic backups - Disaster recovery / one-click restore - Backup integrity validation - Cat-themed admin interface Includes build.sh and .distignore for WordPress-installable release ZIPs.
268 lines
8.2 KiB
PHP
268 lines
8.2 KiB
PHP
<?php
|
|
/**
|
|
* TigerStyle Life9 Encryption Class
|
|
*
|
|
* Military-grade encryption for backup files
|
|
*
|
|
* @package TigerStyleLife9
|
|
* @subpackage Security
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* TigerStyle Life9 Encryption Manager
|
|
*
|
|
* Handles encryption/decryption with cat-themed security
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class TigerStyle_Life9_Encryption {
|
|
|
|
/**
|
|
* Encryption method
|
|
*/
|
|
const ENCRYPTION_METHOD = 'aes-256-gcm';
|
|
|
|
/**
|
|
* Key derivation iterations
|
|
*/
|
|
const PBKDF2_ITERATIONS = 100000;
|
|
|
|
/**
|
|
* Generate encryption key from password
|
|
*
|
|
* @param string $password Password
|
|
* @param string $salt Salt
|
|
* @return string Derived key
|
|
*/
|
|
public static function derive_key($password, $salt) {
|
|
return hash_pbkdf2('sha256', $password, $salt, self::PBKDF2_ITERATIONS, 32, true);
|
|
}
|
|
|
|
/**
|
|
* Generate random salt
|
|
*
|
|
* @param int $length Salt length
|
|
* @return string Random salt
|
|
*/
|
|
public static function generate_salt($length = 32) {
|
|
return random_bytes($length);
|
|
}
|
|
|
|
/**
|
|
* Generate random IV
|
|
*
|
|
* @return string Random IV
|
|
*/
|
|
public static function generate_iv() {
|
|
return random_bytes(openssl_cipher_iv_length(self::ENCRYPTION_METHOD));
|
|
}
|
|
|
|
/**
|
|
* Encrypt data
|
|
*
|
|
* @param string $data Data to encrypt
|
|
* @param string $password Password
|
|
* @return array Encrypted data with metadata
|
|
*/
|
|
public static function encrypt($data, $password) {
|
|
// Generate salt and IV
|
|
$salt = self::generate_salt();
|
|
$iv = self::generate_iv();
|
|
|
|
// Derive key
|
|
$key = self::derive_key($password, $salt);
|
|
|
|
// Encrypt data
|
|
$tag = '';
|
|
$encrypted = openssl_encrypt($data, self::ENCRYPTION_METHOD, $key, OPENSSL_RAW_DATA, $iv, $tag);
|
|
|
|
if ($encrypted === false) {
|
|
throw new Exception(__('🙀 Encryption failed! The cat\'s secret powers are temporarily unavailable.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
return [
|
|
'data' => $encrypted,
|
|
'salt' => $salt,
|
|
'iv' => $iv,
|
|
'tag' => $tag,
|
|
'method' => self::ENCRYPTION_METHOD
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Decrypt data
|
|
*
|
|
* @param array $encrypted_data Encrypted data with metadata
|
|
* @param string $password Password
|
|
* @return string Decrypted data
|
|
*/
|
|
public static function decrypt($encrypted_data, $password) {
|
|
// Extract components
|
|
$data = $encrypted_data['data'];
|
|
$salt = $encrypted_data['salt'];
|
|
$iv = $encrypted_data['iv'];
|
|
$tag = $encrypted_data['tag'];
|
|
$method = $encrypted_data['method'];
|
|
|
|
// Derive key
|
|
$key = self::derive_key($password, $salt);
|
|
|
|
// Decrypt data
|
|
$decrypted = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
|
|
|
|
if ($decrypted === false) {
|
|
throw new Exception(__('🙀 Decryption failed! Wrong password or corrupted data. The cat is suspicious.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
return $decrypted;
|
|
}
|
|
|
|
/**
|
|
* Encrypt file
|
|
*
|
|
* @param string $source_file Source file path
|
|
* @param string $destination_file Destination file path
|
|
* @param string $password Password
|
|
* @return bool Success
|
|
*/
|
|
public static function encrypt_file($source_file, $destination_file, $password) {
|
|
if (!file_exists($source_file)) {
|
|
throw new Exception(__('🙀 Source file not found! The cat cannot encrypt what doesn\'t exist.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
// Read file content
|
|
$data = file_get_contents($source_file);
|
|
if ($data === false) {
|
|
throw new Exception(__('🙀 Cannot read source file! The cat\'s access is denied.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
// Encrypt data
|
|
$encrypted = self::encrypt($data, $password);
|
|
|
|
// Create encrypted file header
|
|
$header = [
|
|
'version' => '1.0',
|
|
'method' => $encrypted['method'],
|
|
'salt' => base64_encode($encrypted['salt']),
|
|
'iv' => base64_encode($encrypted['iv']),
|
|
'tag' => base64_encode($encrypted['tag']),
|
|
'timestamp' => time(),
|
|
'original_size' => strlen($data)
|
|
];
|
|
|
|
// Write encrypted file
|
|
$file_content = "TIGERSTYLE_LIFE9_ENCRYPTED\n";
|
|
$file_content .= json_encode($header) . "\n";
|
|
$file_content .= "DATA_START\n";
|
|
$file_content .= base64_encode($encrypted['data']);
|
|
|
|
$result = file_put_contents($destination_file, $file_content);
|
|
if ($result === false) {
|
|
throw new Exception(__('🙀 Cannot write encrypted file! The cat\'s write access is denied.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Decrypt file
|
|
*
|
|
* @param string $source_file Source encrypted file path
|
|
* @param string $destination_file Destination file path
|
|
* @param string $password Password
|
|
* @return bool Success
|
|
*/
|
|
public static function decrypt_file($source_file, $destination_file, $password) {
|
|
if (!file_exists($source_file)) {
|
|
throw new Exception(__('🙀 Encrypted file not found! The cat cannot decrypt what doesn\'t exist.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
// Read encrypted file
|
|
$content = file_get_contents($source_file);
|
|
if ($content === false) {
|
|
throw new Exception(__('🙀 Cannot read encrypted file! The cat\'s access is denied.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
// Parse file content
|
|
$lines = explode("\n", $content);
|
|
|
|
if ($lines[0] !== 'TIGERSTYLE_LIFE9_ENCRYPTED') {
|
|
throw new Exception(__('🙀 Invalid encrypted file format! This is not a cat-encrypted file.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
$header = json_decode($lines[1], true);
|
|
if (!$header) {
|
|
throw new Exception(__('🙀 Corrupted file header! The cat cannot parse the encryption metadata.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
if ($lines[2] !== 'DATA_START') {
|
|
throw new Exception(__('🙀 Invalid file structure! The cat is confused by this format.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
$encrypted_data = base64_decode($lines[3]);
|
|
|
|
// Prepare decryption data
|
|
$decryption_data = [
|
|
'data' => $encrypted_data,
|
|
'salt' => base64_decode($header['salt']),
|
|
'iv' => base64_decode($header['iv']),
|
|
'tag' => base64_decode($header['tag']),
|
|
'method' => $header['method']
|
|
];
|
|
|
|
// Decrypt data
|
|
$decrypted = self::decrypt($decryption_data, $password);
|
|
|
|
// Write decrypted file
|
|
$result = file_put_contents($destination_file, $decrypted);
|
|
if ($result === false) {
|
|
throw new Exception(__('🙀 Cannot write decrypted file! The cat\'s write access is denied.', 'tigerstyle-life9'));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Generate secure password
|
|
*
|
|
* @param int $length Password length
|
|
* @return string Generated password
|
|
*/
|
|
public static function generate_password($length = 32) {
|
|
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
$password = '';
|
|
|
|
for ($i = 0; $i < $length; $i++) {
|
|
$password .= $characters[random_int(0, strlen($characters) - 1)];
|
|
}
|
|
|
|
return $password;
|
|
}
|
|
|
|
/**
|
|
* Hash password for storage
|
|
*
|
|
* @param string $password Password to hash
|
|
* @return string Hashed password
|
|
*/
|
|
public static function hash_password($password) {
|
|
return password_hash($password, PASSWORD_ARGON2ID);
|
|
}
|
|
|
|
/**
|
|
* Verify password against hash
|
|
*
|
|
* @param string $password Password to verify
|
|
* @param string $hash Hash to verify against
|
|
* @return bool Verification result
|
|
*/
|
|
public static function verify_password($password, $hash) {
|
|
return password_verify($password, $hash);
|
|
}
|
|
} |