//! 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 { 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::() 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::() { // 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 = 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, }