Compare commits

...

2 Commits

Author SHA1 Message Date
56f957a346 remove duplicate hardware 2025-08-14 23:40:32 +08:00
2c90276e3e fix: resolve cross-compilation issues for ARM64 Linux
- Replace OpenSSL with rustls for better cross-compilation support
- Fix tpm_attestation field name typos (was tmp_attestation)
- Add missing Debug traits to FramePool structs
- Fix borrow checker issue in device registration
- Add missing module declarations in main.rs

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-14 00:27:46 +08:00
8 changed files with 92 additions and 311 deletions

View File

@ -109,7 +109,7 @@ dependencies = [
"nom",
"num-traits",
"rusticata-macros",
"thiserror 1.0.69",
"thiserror",
"time",
]
@ -630,21 +630,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@ -958,16 +943,17 @@ dependencies = [
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
name = "hyper-rustls"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"bytes",
"futures-util",
"http 0.2.12",
"hyper",
"native-tls",
"rustls 0.21.12",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.24.1",
]
[[package]]
@ -1215,11 +1201,9 @@ checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
dependencies = [
"base64 0.22.1",
"js-sys",
"pem",
"ring",
"serde",
"serde_json",
"simple_asn1",
]
[[package]]
@ -1375,7 +1359,7 @@ dependencies = [
"sys-info",
"sysinfo",
"tempfile",
"thiserror 1.0.69",
"thiserror",
"tokio",
"tokio-tungstenite",
"toml",
@ -1431,23 +1415,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "nix"
version = "0.29.0"
@ -1564,50 +1531,12 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "openssl"
version = "0.10.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
dependencies = [
"bitflags 2.9.1",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
@ -1643,16 +1572,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "pem"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
dependencies = [
"base64 0.22.1",
"serde",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -1671,12 +1590,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "png"
version = "0.17.16"
@ -1833,7 +1746,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror 1.0.69",
"thiserror",
]
[[package]]
@ -1895,16 +1808,16 @@ dependencies = [
"http 0.2.12",
"http-body",
"hyper",
"hyper-tls",
"hyper-rustls",
"ipnet",
"js-sys",
"log",
"mime",
"mime_guess",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.21.12",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
@ -1912,12 +1825,13 @@ dependencies = [
"sync_wrapper",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.24.1",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"winreg",
]
@ -1982,6 +1896,18 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "rustls"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.22.4"
@ -2052,6 +1978,16 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.8"
@ -2102,6 +2038,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "security-framework"
version = "2.11.1"
@ -2230,18 +2176,6 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simple_asn1"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb"
dependencies = [
"num-bigint",
"num-traits",
"thiserror 2.0.14",
"time",
]
[[package]]
name = "slab"
version = "0.4.10"
@ -2385,16 +2319,7 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e"
dependencies = [
"thiserror-impl 2.0.14",
"thiserror-impl",
]
[[package]]
@ -2408,17 +2333,6 @@ dependencies = [
"syn",
]
[[package]]
name = "thiserror-impl"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.9"
@ -2512,12 +2426,12 @@ dependencies = [
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"native-tls",
"rustls 0.21.12",
"tokio",
]
@ -2544,7 +2458,7 @@ dependencies = [
"rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tokio-rustls 0.25.0",
"tungstenite",
]
@ -2626,7 +2540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [
"crossbeam-channel",
"thiserror 1.0.69",
"thiserror",
"time",
"tracing-subscriber",
]
@ -2718,7 +2632,7 @@ dependencies = [
"rustls-native-certs",
"rustls-pki-types",
"sha1",
"thiserror 1.0.69",
"thiserror",
"url",
"utf-8",
]
@ -2793,12 +2707,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
@ -2910,6 +2818,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "weezl"
version = "0.1.10"
@ -3223,7 +3137,7 @@ dependencies = [
"nom",
"oid-registry",
"rusticata-macros",
"thiserror 1.0.69",
"thiserror",
"time",
]

View File

@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
clap = { version = "4.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["json", "multipart"] }
reqwest = { version = "0.11", features = ["json", "multipart", "rustls-tls"], default-features = false }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
@ -30,7 +30,7 @@ sha2 = "0.10"
base64 = "0.22"
rand = "0.8"
hmac = "0.12"
jsonwebtoken = "9.2"
jsonwebtoken = { version = "9.2", default-features = false }
hex = "0.4"
ring = "0.17"
rustls = { version = "0.23", features = ["ring"] }

View File

@ -227,7 +227,8 @@ impl DeviceRegistrationClient {
/// Claims the device using the registration token
async fn claim_device(&mut self) -> Result<()> {
let token = self.registration_token.as_ref()
.context("No registration token available")?;
.context("No registration token available")?
.clone();
info!("Claiming device with token: {}", token.claim_id);
self.set_state(RegistrationState::Claiming).await;
@ -246,7 +247,7 @@ impl DeviceRegistrationClient {
"board_serial": fingerprint.board_serial,
"mac_addresses": fingerprint.mac_addresses,
"disk_uuid": fingerprint.disk_uuid,
"tmp_attestation": fingerprint.tmp_attestation,
"tpm_attestation": fingerprint.tpm_attestation,
},
"device_info": {
"model": format!("{} {}", fingerprint.system_info.os_name, fingerprint.system_info.architecture),

View File

@ -81,10 +81,12 @@ impl Drop for PooledFrameBuffer {
}
/// Core frame buffer pool for eliminating allocations
#[derive(Debug)]
pub struct FramePool {
inner: Arc<Mutex<FramePoolInner>>,
}
#[derive(Debug)]
struct FramePoolInner {
available_buffers: VecDeque<BytesMut>,
pool_capacity: usize,

View File

@ -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<String> {
// 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<String> {
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<String> {
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<String> {
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<String> {
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");
}
}

View File

@ -78,7 +78,7 @@ impl HardwareFingerprintService {
let board_serial = self.get_board_serial().await?;
let mac_addresses = self.get_mac_addresses().await?;
let disk_uuid = self.get_primary_disk_uuid().await?;
let tmp_attestation = self.get_tpm_attestation().await.ok();
let tpm_attestation = self.get_tpm_attestation().await.ok();
let system_info = self.collect_system_info().await?;
// Compute hash from core identifiers
@ -89,7 +89,7 @@ impl HardwareFingerprintService {
board_serial,
mac_addresses,
disk_uuid,
tmp_attestation: tmp_attestation,
tpm_attestation: tpm_attestation,
system_info,
computed_hash,
};

View File

@ -1,9 +1,11 @@
use clap::{Parser, Subcommand};
use anyhow::Result;
mod hardware;
mod config;
mod api;
mod frame_data;
mod frame_pool;
mod memory_monitor;
mod events;
mod app;
mod camera;
@ -14,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")]
@ -142,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
@ -181,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);
}
}
@ -356,8 +364,8 @@ async fn test_hardware_fingerprint() -> Result<()> {
println!(" Board Serial: {}", fingerprint.board_serial);
println!(" MAC Addresses: {}", fingerprint.mac_addresses.join(", "));
println!(" Disk UUID: {}", fingerprint.disk_uuid);
if let Some(tmp) = &fingerprint.tmp_attestation {
println!(" TPM Attestation: {}...", &tmp[..20]);
if let Some(tpm) = &fingerprint.tpm_attestation {
println!(" TPM Attestation: {}...", &tpm[..20]);
} else {
println!(" TPM Attestation: Not available");
}

View File

@ -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');