hardware_id/
lib.rs

1// rust-hwid
2// (c) 2020 tilda, under MIT license
3
4//! Get a "Hardware ID" for the host machine. This is a UUID
5//! which is intended to uniquely represent this entity.
6
7use thiserror::Error;
8
9/// Possible failure cases for [get_id()].
10#[derive(Debug, Error)]
11pub enum HwIdError {
12    /// Could not detect a hardware id. This might be caused
13    /// by a misconfigured system or by this feature not
14    /// being supported by the system or platform.
15    #[error("no HWID was found on system")]
16    NotFound,
17    /// Found a putative HWID, but something was wrong with
18    /// it.  The `String` argument contains a path or other
19    /// identifier at which the HWID was found. This will
20    /// usually indicate something is really wrong with the
21    /// system.
22    #[error("{0:?}: contains malformed HWID")]
23    Malformed(String),
24}
25
26#[cfg(target_os = "windows")]
27mod hwid {
28    use super::*;
29    use winreg::enums::{HKEY_LOCAL_MACHINE, KEY_QUERY_VALUE};
30
31    /// Get the hardware ID of this machine. The HWID is
32    /// obtained from the Windows registry at location
33    /// `\\\\SOFTWARE\\Microsoft\\Cryptography\\MachineGuid`.
34    pub fn get_id() -> Result<std::string::String, HwIdError> {
35        // escaping is fun, right? right???
36        let hive = winreg::RegKey::predef(HKEY_LOCAL_MACHINE)
37            .open_subkey_with_flags("Software\\Microsoft\\Cryptography", KEY_QUERY_VALUE)
38            .or(Err(HwIdError::NotFound))?;
39        let id = hive.get_value("MachineGuid").or(Err(HwIdError::NotFound))?;
40        Ok(id)
41    }
42}
43
44#[cfg(target_os = "macos")]
45mod hwid {
46    use super::*;
47
48    /// Get the hardware ID of this machine. The HWID is
49    /// obtained by running
50    ///
51    /// ```text
52    /// ioreg -rd1 -c IOExpertPlatformDevice
53    /// ```
54    ///
55    /// and returning the result.
56    pub fn get_id() -> Result<std::string::String, HwIdError> {
57        let cmd = std::process::Command::new("ioreg")
58            .arg("-rd1")
59            .arg("-c")
60            .arg("IOPlatformExpertDevice")
61            .output()
62            .or(Err(HwIdError::NotFound))?
63            .stdout;
64        let out = String::from_utf8(cmd).or(Err(HwIdError::Malformed(String::from("ioreg"))))?;
65        match out
66            .lines()
67            .find(|l| l.contains("IOPlatformUUID"))
68            .unwrap_or("")
69            .split('=')
70            .nth(1)
71            .unwrap_or("")
72            .split('\"')
73            .nth(1)
74        {
75            None => Err(HwIdError::NotFound),
76            Some(id) => {
77                if id.is_empty() {
78                    Err(HwIdError::Malformed(String::from("ioreg")))
79                } else {
80                    Ok(id.to_string())
81                }
82            }
83        }
84    }
85}
86
87#[cfg(target_os = "linux")]
88mod hwid {
89    use super::*;
90
91    /// Get the hardware ID of this machine. The HWID is
92    /// obtained from `/var/lib/dbus/machine-id`, or failing
93    /// that from `/etc/machine-id`.
94    pub fn get_id() -> Result<std::string::String, HwIdError> {
95        let paths = ["/var/lib/dbus/machine-id", "/etc/machine-id"];
96        for p in paths {
97            if let Ok(id_contents) = std::fs::read_to_string(p) {
98                let id_str = id_contents
99                    .lines()
100                    .next()
101                    .ok_or_else(|| HwIdError::Malformed(id_contents.to_string()))?;
102                return Ok(id_str.to_string());
103            }
104        }
105        Err(HwIdError::NotFound)
106    }
107}
108
109#[cfg(target_os = "freebsd")]
110#[cfg(target_os = "dragonfly")]
111#[cfg(target_os = "openbsd")]
112#[cfg(target_os = "netbsd")]
113mod hwid {
114    pub fn get_id() -> std::string::String {
115        unimplemented!("*BSD support is not implemented")
116    }
117}
118
119pub use crate::hwid::get_id;