- Tauri 2.0 based graphical installer - Access Key parsing with AES-256-GCM encryption - Windows Service installation via sc.exe - WinTUN driver extraction from embedded resources - System requirements checking (admin, AES-NI, OS version) - Modern dark UI with step-by-step wizard flow - Country/region selection for SNI mimicry
226 lines
5.9 KiB
Rust
226 lines
5.9 KiB
Rust
//! System requirements checking
|
|
//!
|
|
//! Verifies that the target system meets all requirements for OSTP Client.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// System check result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SystemCheckResult {
|
|
pub wintun_installed: bool,
|
|
pub aes_ni_supported: bool,
|
|
pub admin_privileges: bool,
|
|
pub os_version: String,
|
|
}
|
|
use anyhow::Result;
|
|
|
|
/// Run all system checks
|
|
pub async fn run_checks() -> Result<SystemCheckResult> {
|
|
Ok(SystemCheckResult {
|
|
wintun_installed: check_wintun_installed(),
|
|
aes_ni_supported: check_aes_ni_support(),
|
|
admin_privileges: check_admin_privileges(),
|
|
os_version: get_os_version(),
|
|
})
|
|
}
|
|
|
|
/// Check if wintun.dll is already installed
|
|
fn check_wintun_installed() -> bool {
|
|
crate::wintun::is_installed()
|
|
}
|
|
|
|
/// Check if CPU supports AES-NI instructions
|
|
fn check_aes_ni_support() -> bool {
|
|
#[cfg(target_arch = "x86_64")]
|
|
{
|
|
// Check CPUID for AES-NI support
|
|
return is_x86_feature_detected!("aes");
|
|
}
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
{
|
|
// ARM64 typically has crypto extensions
|
|
// Check for ARMv8 Crypto extensions
|
|
#[cfg(target_feature = "aes")]
|
|
return true;
|
|
|
|
#[cfg(not(target_feature = "aes"))]
|
|
return false;
|
|
}
|
|
|
|
// Fallback: assume software AES is acceptable
|
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
|
true
|
|
}
|
|
|
|
/// Check if running with administrator privileges
|
|
fn check_admin_privileges() -> bool {
|
|
#[cfg(windows)]
|
|
{
|
|
use std::ptr;
|
|
|
|
// Use Windows API to check elevation
|
|
unsafe {
|
|
let mut token_handle: *mut std::ffi::c_void = ptr::null_mut();
|
|
let current_process = GetCurrentProcess();
|
|
|
|
if OpenProcessToken(
|
|
current_process,
|
|
TOKEN_QUERY,
|
|
&mut token_handle,
|
|
) == 0 {
|
|
return false;
|
|
}
|
|
|
|
let mut elevation = TOKEN_ELEVATION { TokenIsElevated: 0 };
|
|
let mut size: u32 = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
|
|
|
|
let result = GetTokenInformation(
|
|
token_handle,
|
|
TokenElevation,
|
|
&mut elevation as *mut _ as *mut _,
|
|
size,
|
|
&mut size,
|
|
);
|
|
|
|
CloseHandle(token_handle);
|
|
|
|
result != 0 && elevation.TokenIsElevated != 0
|
|
}
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
{
|
|
// On non-Windows, check if running as root
|
|
unsafe { libc::geteuid() == 0 }
|
|
}
|
|
}
|
|
|
|
/// Get Windows version string
|
|
fn get_os_version() -> String {
|
|
#[cfg(windows)]
|
|
{
|
|
use winreg::{enums::*, RegKey};
|
|
|
|
if let Ok(hklm) = RegKey::predef(HKEY_LOCAL_MACHINE)
|
|
.open_subkey(r"SOFTWARE\Microsoft\Windows NT\CurrentVersion")
|
|
{
|
|
let product_name: String = hklm.get_value("ProductName").unwrap_or_default();
|
|
let build: String = hklm.get_value("CurrentBuild").unwrap_or_default();
|
|
|
|
if !product_name.is_empty() {
|
|
return format!("{} (Build {})", product_name, build);
|
|
}
|
|
}
|
|
|
|
"Windows (Unknown Version)".to_string()
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
{
|
|
"Non-Windows OS".to_string()
|
|
}
|
|
}
|
|
|
|
/// Check minimum Windows version (Windows 10 1607+)
|
|
#[allow(dead_code)]
|
|
pub fn check_minimum_version() -> bool {
|
|
#[cfg(windows)]
|
|
{
|
|
use winreg::{enums::*, RegKey};
|
|
|
|
if let Ok(hklm) = RegKey::predef(HKEY_LOCAL_MACHINE)
|
|
.open_subkey(r"SOFTWARE\Microsoft\Windows NT\CurrentVersion")
|
|
{
|
|
let build: String = hklm.get_value("CurrentBuild").unwrap_or_default();
|
|
if let Ok(build_num) = build.parse::<u32>() {
|
|
// Windows 10 1607 is build 14393
|
|
return build_num >= 14393;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
true
|
|
}
|
|
|
|
/// Check available disk space (need at least 50MB)
|
|
#[allow(dead_code)]
|
|
pub fn check_disk_space(path: &std::path::Path) -> bool {
|
|
#[cfg(windows)]
|
|
{
|
|
use std::os::windows::ffi::OsStrExt;
|
|
use std::ffi::OsStr;
|
|
|
|
let path_wide: Vec<u16> = OsStr::new(path)
|
|
.encode_wide()
|
|
.chain(std::iter::once(0))
|
|
.collect();
|
|
|
|
let mut free_bytes: u64 = 0;
|
|
let mut total_bytes: u64 = 0;
|
|
let mut total_free_bytes: u64 = 0;
|
|
|
|
unsafe {
|
|
if GetDiskFreeSpaceExW(
|
|
path_wide.as_ptr(),
|
|
&mut free_bytes,
|
|
&mut total_bytes,
|
|
&mut total_free_bytes,
|
|
) != 0 {
|
|
// Need at least 50MB
|
|
return free_bytes >= 50 * 1024 * 1024;
|
|
}
|
|
}
|
|
|
|
true // Assume OK if check fails
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
{
|
|
let _ = path;
|
|
true
|
|
}
|
|
}
|
|
|
|
// Windows API bindings
|
|
#[cfg(windows)]
|
|
unsafe extern "system" {
|
|
fn GetCurrentProcess() -> *mut std::ffi::c_void;
|
|
fn OpenProcessToken(
|
|
process: *mut std::ffi::c_void,
|
|
access: u32,
|
|
token: *mut *mut std::ffi::c_void,
|
|
) -> i32;
|
|
fn GetTokenInformation(
|
|
token: *mut std::ffi::c_void,
|
|
info_class: u32,
|
|
info: *mut std::ffi::c_void,
|
|
info_len: u32,
|
|
return_len: *mut u32,
|
|
) -> i32;
|
|
fn CloseHandle(handle: *mut std::ffi::c_void) -> i32;
|
|
#[allow(dead_code)]
|
|
fn GetDiskFreeSpaceExW(
|
|
directory: *const u16,
|
|
free_bytes: *mut u64,
|
|
total_bytes: *mut u64,
|
|
total_free_bytes: *mut u64,
|
|
) -> i32;
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
const TOKEN_QUERY: u32 = 0x0008;
|
|
#[cfg(windows)]
|
|
#[allow(non_upper_case_globals)]
|
|
const TokenElevation: u32 = 20;
|
|
|
|
#[cfg(windows)]
|
|
#[repr(C)]
|
|
#[allow(non_snake_case)]
|
|
struct TOKEN_ELEVATION {
|
|
TokenIsElevated: u32,
|
|
}
|