error( 'Encryption failed: ' . openssl_error_string(), 'encryption' ); return ''; } // Prepend IV to ciphertext so we can extract it on decrypt. // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode return base64_encode( $iv . $ciphertext ); } /** * Decrypt a previously encrypted string. * * @param string $encrypted Base64-encoded ciphertext with IV prepended. * * @return string The original plaintext, or empty string on failure. */ public static function decrypt( string $encrypted ): string { if ( '' === $encrypted ) { return ''; } $key = self::get_key(); $iv_len = (int) openssl_cipher_iv_length( self::METHOD ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode $raw = base64_decode( $encrypted, true ); if ( false === $raw || strlen( $raw ) <= $iv_len ) { PRAutoBlogger_Logger::instance()->error( 'Decryption failed: invalid ciphertext format.', 'encryption' ); return ''; } $iv = substr( $raw, 0, $iv_len ); $ciphertext = substr( $raw, $iv_len ); $plaintext = openssl_decrypt( $ciphertext, self::METHOD, $key, OPENSSL_RAW_DATA, $iv ); if ( false === $plaintext ) { PRAutoBlogger_Logger::instance()->error( 'Decryption failed: ' . openssl_error_string(), 'encryption' ); return ''; } return $plaintext; } /** * Derive the encryption key from WordPress salts. * * @return string 32-byte key suitable for AES-256. */ private static function get_key(): string { return hash( 'sha256', wp_salt( 'auth' ), true ); } }