xmid/
lib.rs

1//! Creating a Machine ID hash for MacOS/Windows/Linux.
2//!
3//! ```
4//! let machine_id = mid::get("mySecretKey").unwrap();
5//! ```
6
7mod errors;
8mod linux;
9mod macos;
10mod utils;
11mod windows;
12
13use errors::MIDError;
14use hmac_sha256::HMAC;
15
16#[cfg(target_os = "linux")]
17use linux::get_mid_result;
18#[cfg(target_os = "macos")]
19use macos::get_additional_data;
20#[cfg(target_os = "macos")]
21use macos::get_mid_result;
22#[cfg(target_os = "windows")]
23use windows::get_mid_result;
24
25#[derive(Debug)]
26pub struct MidData {
27    pub key: String,
28    pub result: Vec<String>,
29    pub hash: String,
30}
31
32#[derive(Debug)]
33#[cfg(target_os = "macos")]
34pub struct AdditionalData {
35    pub username: String,
36    pub hostname: String,
37    pub os_name: String,
38    pub os_version: String,
39    pub os_full: String,
40    pub chip: String,
41    pub memsize: u8,
42    pub cpu_core_count: u8,
43    pub languages: Vec<String>,
44}
45
46/// Gets unique platform metrics and returns a `Result`, which can be a MID hash (SHA-256) or a `MIDError`.
47///
48/// # Errors
49///
50/// Returns [`Err`] if an error occurred while creating the MachineID.
51///
52/// # Example
53///
54/// ```
55/// fn get_machine_id() -> Result<String, String> {
56///   match mid::get("mySecretKey") {
57///       Ok(mid) => Ok(mid),
58///       Err(err) => {
59///           println!("MID error: {}", err.to_string());
60///           Err(err.to_string())
61///       }
62///   }
63/// }
64/// ```
65pub fn get(key: &str) -> Result<String, MIDError> {
66    match data(key) {
67        Ok(mid) => Ok(mid.hash),
68        Err(err) => Err(err),
69    }
70}
71
72/// Returns MID key/result/hash as [`MidData`]
73///
74/// # Errors
75///
76/// Returns [`Err`] if an error occurred while creating the MachineID.
77///
78/// # Example
79///
80/// ```
81/// let mid_data = mid::data("mySecretKey").unwrap();
82/// ```
83pub fn data(key: &str) -> Result<MidData, MIDError> {
84    if key.is_empty() {
85        return Err(MIDError::EmptyMidKey);
86    }
87
88    match get_mid_result() {
89        Ok(mid) => {
90            let mid_result: Vec<String> = mid.split('|').map(|s| s.to_string()).collect();
91
92            let hmac_result = HMAC::mac(mid.as_bytes(), key.as_bytes());
93            let mid_hash = hex::encode(hmac_result);
94
95            Ok(MidData {
96                key: String::from(key),
97                result: mid_result,
98                hash: mid_hash,
99            })
100        }
101        Err(err) => Err(err),
102    }
103}
104
105/// Returns additional device data that is not involved in generating the device hash as [`AdditionalData`]
106///
107/// # Errors
108///
109/// Returns [`Err`] if an error occurred while retrieving additional data.
110///
111/// # Example
112///
113/// ```
114/// let additional_data = mid::additional_data().unwrap();
115/// println!("Username: {}", additional_data.username);
116/// println!("Hostname: {}", additional_data.hostname);
117/// println!("OS Name: {}", additional_data.os_name);
118/// println!("OS Version: {}", additional_data.os_version);
119/// println!("OS Full: {}", additional_data.os_full);
120/// println!("Chip: {}", additional_data.chip);
121/// println!("Memory Size: {}", additional_data.memsize);
122/// println!("CPU Core Count: {}", additional_data.cpu_core_count);
123/// println!("Languages: {:?}", additional_data.languages);
124/// ```
125#[cfg(target_os = "macos")]
126pub fn additional_data() -> Result<AdditionalData, MIDError> {
127    match get_additional_data() {
128        Ok(additional_data) => Ok(additional_data),
129        Err(err) => Err(err),
130    }
131}
132
133/// Output the MID key/result/hash to the console in `debug_assertions` mode.
134///
135/// `MID key` - The secret key for hashing
136///
137/// `MID result` - Array of OS parameters
138///
139/// `MID hash` - SHA-256 hash from result
140///
141/// # Example
142///
143/// ```
144/// mid::print("mySecretKey");
145/// ```
146pub fn print(key: &str) {
147    match data(key) {
148        Ok(mid) => {
149            debug!("MID.print[key]: {}", mid.key);
150            debug!("MID.print[result]: {:?}", mid.result);
151            debug!("MID.print[hash]: {}", mid.hash);
152        }
153        Err(err) => debug!("MID.print[error]: {}", err),
154    }
155}
156
157#[test]
158fn test_mid_operations() {
159    match get("mykey") {
160        Ok(mid) => debug!("MID.get: {}", mid),
161        Err(err) => debug!("MID.get[error]: {}", err),
162    }
163
164    match data("mykey") {
165        Ok(log_data) => debug!("MID.data: {:?}", log_data),
166        Err(err) => debug!("MID.data[error]: {}", err),
167    }
168
169    #[cfg(target_os = "macos")]
170    match additional_data() {
171        Ok(log_data) => debug!("MID.additional_data: {:?}", log_data),
172        Err(err) => debug!("MID.additional_data[error]: {}", err),
173    }
174
175    print("mykey");
176}