From 56f957a346474d0bfeae1b1d7fc576b34a28aa85 Mon Sep 17 00:00:00 2001 From: grabbit Date: Thu, 14 Aug 2025 23:40:32 +0800 Subject: [PATCH] remove duplicate hardware --- meteor-edge-client/src/hardware.rs | 144 ------------------ meteor-edge-client/src/main.rs | 21 ++- .../services/device-security.service.ts | 8 +- 3 files changed, 17 insertions(+), 156 deletions(-) delete mode 100644 meteor-edge-client/src/hardware.rs diff --git a/meteor-edge-client/src/hardware.rs b/meteor-edge-client/src/hardware.rs deleted file mode 100644 index 9610438..0000000 --- a/meteor-edge-client/src/hardware.rs +++ /dev/null @@ -1,144 +0,0 @@ -use anyhow::{Context, Result}; -use std::fs; - -/// Extracts a unique hardware identifier from the system -/// This function reads /proc/cpuinfo to find a stable CPU serial number -pub fn get_hardware_id() -> Result { - // Try to read /proc/cpuinfo first (common on Raspberry Pi and other ARM systems) - if let Ok(hardware_id) = read_cpu_serial() { - return Ok(hardware_id); - } - - // Fallback: try to read machine-id - if let Ok(machine_id) = read_machine_id() { - return Ok(machine_id); - } - - // Last resort: generate a warning and use hostname + MAC address hash - eprintln!("Warning: Could not read CPU serial or machine-id, using fallback method"); - get_fallback_id() -} - -/// Reads CPU serial number from /proc/cpuinfo -/// This is the most reliable method on Raspberry Pi systems -fn read_cpu_serial() -> Result { - let cpuinfo = fs::read_to_string("/proc/cpuinfo") - .context("Failed to read /proc/cpuinfo")?; - - for line in cpuinfo.lines() { - if line.starts_with("Serial") { - if let Some(serial) = line.split(':').nth(1) { - let serial = serial.trim(); - if !serial.is_empty() && serial != "0000000000000000" { - return Ok(format!("CPU_{}", serial)); - } - } - } - } - - anyhow::bail!("No valid CPU serial found in /proc/cpuinfo") -} - -/// Reads machine ID from /etc/machine-id (systemd systems) -fn read_machine_id() -> Result { - let machine_id = fs::read_to_string("/etc/machine-id") - .context("Failed to read /etc/machine-id")?; - - let machine_id = machine_id.trim(); - if machine_id.len() >= 8 { - Ok(format!("MACHINE_{}", &machine_id[..16])) - } else { - anyhow::bail!("Invalid machine-id format") - } -} - -/// Fallback method to generate a hardware ID -/// Uses hostname + network interface information -fn get_fallback_id() -> Result { - use std::process::Command; - - // Get hostname - let hostname_output = Command::new("hostname") - .output() - .context("Failed to execute hostname command")?; - - let hostname = String::from_utf8_lossy(&hostname_output.stdout) - .trim() - .to_string(); - - // Try to get MAC address from network interfaces - if let Ok(mac) = get_primary_mac_address() { - return Ok(format!("FALLBACK_{}_{}", hostname, mac)); - } - - // Very last resort: just use hostname with timestamp - let timestamp = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_secs(); - - Ok(format!("FALLBACK_{}_{}", hostname, timestamp)) -} - -/// Attempts to get the MAC address of the primary network interface -fn get_primary_mac_address() -> Result { - let interfaces_dir = "/sys/class/net"; - let entries = fs::read_dir(interfaces_dir) - .context("Failed to read network interfaces directory")?; - - for entry in entries { - let entry = entry?; - let interface_name = entry.file_name(); - let interface_name = interface_name.to_string_lossy(); - - // Skip loopback and common virtual interfaces - if interface_name == "lo" || interface_name.starts_with("docker") - || interface_name.starts_with("veth") { - continue; - } - - let mac_path = format!("{}/{}/address", interfaces_dir, interface_name); - if let Ok(mac) = fs::read_to_string(&mac_path) { - let mac = mac.trim().replace(':', ""); - if mac.len() == 12 && mac != "000000000000" { - return Ok(mac.to_uppercase()); - } - } - } - - anyhow::bail!("No valid MAC address found") -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hardware_id_format() { - // Test that we can get some kind of hardware ID - // The exact format will depend on the system, but it should not be empty - let result = get_hardware_id(); - match result { - Ok(id) => { - assert!(!id.is_empty(), "Hardware ID should not be empty"); - assert!(id.len() >= 8, "Hardware ID should be at least 8 characters"); - println!("Hardware ID: {}", id); - } - Err(e) => { - println!("Could not get hardware ID: {}", e); - // On systems without the expected files, this might fail - // That's okay for testing purposes - } - } - } - - #[test] - fn test_fallback_id() { - let result = get_fallback_id(); - assert!(result.is_ok(), "Fallback ID generation should always work"); - - let id = result.unwrap(); - assert!(id.starts_with("FALLBACK_"), "Fallback ID should have correct prefix"); - assert!(id.len() > 10, "Fallback ID should be reasonably long"); - } -} \ No newline at end of file diff --git a/meteor-edge-client/src/main.rs b/meteor-edge-client/src/main.rs index 6e5734a..d4dc27e 100644 --- a/meteor-edge-client/src/main.rs +++ b/meteor-edge-client/src/main.rs @@ -1,7 +1,6 @@ use clap::{Parser, Subcommand}; use anyhow::Result; -mod hardware; mod config; mod api; mod frame_data; @@ -17,10 +16,10 @@ mod hardware_fingerprint; mod device_registration; mod websocket_client; -use hardware::get_hardware_id; use config::{Config, ConfigManager}; use api::ApiClient; use app::Application; +use hardware_fingerprint::HardwareFingerprintService; #[derive(Parser)] #[command(name = "meteor-edge-client")] @@ -145,7 +144,9 @@ async fn register_device(jwt_token: String, api_url: String) -> Result<()> { // Get hardware ID println!("🔍 Reading hardware identifier..."); - let hardware_id = get_hardware_id()?; + let mut fingerprint_service = HardwareFingerprintService::new(); + let fingerprint = fingerprint_service.generate_fingerprint().await?; + let hardware_id = fingerprint.computed_hash[..16].to_string(); println!(" Hardware ID: {}", hardware_id); // Create API client @@ -184,13 +185,17 @@ async fn show_status() -> Result<()> { println!("📊 Meteor Edge Client Status"); println!("============================"); - // Show hardware information - match get_hardware_id() { - Ok(hardware_id) => { - println!("🔧 Hardware ID: {}", hardware_id); + // Show hardware information using the new fingerprint service + let mut fingerprint_service = HardwareFingerprintService::new(); + match fingerprint_service.generate_fingerprint().await { + Ok(fingerprint) => { + println!("🔧 Hardware ID: {}", fingerprint.computed_hash[..16].to_string()); + println!(" CPU ID: {}", fingerprint.cpu_id); + println!(" Board Serial: {}", fingerprint.board_serial); + println!(" MAC Addresses: {}", fingerprint.mac_addresses.join(", ")); } Err(e) => { - println!("❌ Could not read hardware ID: {}", e); + println!("❌ Could not read hardware fingerprint: {}", e); } } diff --git a/meteor-web-backend/src/devices/services/device-security.service.ts b/meteor-web-backend/src/devices/services/device-security.service.ts index 364434b..78a3091 100644 --- a/meteor-web-backend/src/devices/services/device-security.service.ts +++ b/meteor-web-backend/src/devices/services/device-security.service.ts @@ -12,7 +12,7 @@ export interface HardwareFingerprint { board_serial: string; mac_addresses: string[]; disk_uuid: string; - tmp_attestation?: string; + tpm_attestation?: string; } export interface SecurityChallenge { @@ -131,8 +131,8 @@ export class DeviceSecurityService { } // TPM attestation validation if available - if (fingerprint.tmp_attestation) { - const tpmValid = await this.validateTpmAttestation(fingerprint.tmp_attestation); + if (fingerprint.tpm_attestation) { + const tpmValid = await this.validateTpmAttestation(fingerprint.tpm_attestation); if (!tpmValid) { riskFactors.push('TPM attestation validation failed'); confidence *= 0.7; @@ -324,7 +324,7 @@ export class DeviceSecurityService { fingerprint.board_serial, ...fingerprint.mac_addresses.sort(), fingerprint.disk_uuid, - fingerprint.tmp_attestation || '', + fingerprint.tpm_attestation || '', ].join('|'); return crypto.createHash('sha256').update(data).digest('hex');