use sha2::{Digest, Sha256};
pub fn machine_fingerprint() -> Option<String> {
let raw = raw_machine_id()?;
let hash = Sha256::digest(raw.as_bytes());
Some(hex::encode(hash))
}
fn raw_machine_id() -> Option<String> {
#[cfg(target_os = "linux")]
{
if let Ok(id) = std::fs::read_to_string("/etc/machine-id") {
let trimmed = id.trim().to_string();
if !trimmed.is_empty() {
return Some(trimmed);
}
}
if let Ok(id) = std::fs::read_to_string("/var/lib/dbus/machine-id") {
let trimmed = id.trim().to_string();
if !trimmed.is_empty() {
return Some(trimmed);
}
}
}
#[cfg(target_os = "windows")]
{
if let Ok(output) = std::process::Command::new("reg")
.args([
"query",
r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography",
"/v",
"MachineGuid",
])
.output()
{
let text = String::from_utf8_lossy(&output.stdout);
for line in text.lines() {
if let Some(pos) = line.find("REG_SZ") {
let val = line[pos + 6..].trim();
let val = val.trim_start_matches('{').trim_end_matches('}');
if !val.is_empty() {
return Some(val.to_string());
}
}
}
}
}
#[cfg(target_os = "macos")]
{
if let Ok(output) = std::process::Command::new("ioreg")
.args(["-d2", "-c", "IOPlatformExpertDevice"])
.output()
{
let text = String::from_utf8_lossy(&output.stdout);
for line in text.lines() {
if let Some(pos) = line.find("IOPlatformUUID") {
let remainder = &line[pos..];
if let Some(q1) = remainder.find('"') {
let after_key = &remainder[q1 + 1..];
if let Some(q2) = after_key.find('"') {
let after_eq = &after_key[q2 + 1..];
if let Some(q3) = after_eq.find('"') {
if let Some(q4) = after_eq[q3 + 1..].find('"') {
let uuid = &after_eq[q3 + 1..q3 + 1 + q4];
return Some(uuid.to_string());
}
}
}
}
}
}
}
}
let host = hostname::get().ok().and_then(|h| h.into_string().ok())?;
Some(format!(
"{}:{}:{}",
host,
std::env::consts::OS,
std::env::consts::ARCH
))
}
mod hostname {
use std::ffi::OsString;
pub fn get() -> std::io::Result<OsString> {
#[cfg(windows)]
{
std::env::var("COMPUTERNAME")
.map(OsString::from)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::NotFound, "COMPUTERNAME"))
}
#[cfg(unix)]
{
for path in ["/proc/sys/kernel/hostname", "/etc/hostname"] {
if let Ok(s) = std::fs::read_to_string(path) {
let trimmed = s.trim().to_string();
if !trimmed.is_empty() {
return Ok(OsString::from(trimmed));
}
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"hostname files",
))
}
#[cfg(not(any(windows, unix)))]
{
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"unsupported platform",
))
}
}
}