machineid_rs/
lib.rs

1//! Get an encrypted unique MachineID/HWID/UUID.
2//!
3//! This crate is inspired by .Net DeviceId
4//!
5//! You can add all the components you need without admin permissions.
6//!
7//! ```
8//! use machineid_rs::{IdBuilder, Encryption, HWIDComponent};
9//!
10//! // There are 3 different encryption types: MD5, SHA1 and SHA256.
11//! let mut builder = IdBuilder::new(Encryption::MD5);
12//!
13//! builder.add_component(HWIDComponent::SystemID).add_component(HWIDComponent::CPUCores);
14//!
15//! let hwid = builder.build("mykey").unwrap();
16
17#![allow(non_snake_case)]
18
19mod errors;
20mod linux;
21mod utils;
22mod windows;
23mod macos;
24
25use errors::HWIDError;
26#[cfg(target_os = "linux")]
27use linux::{get_disk_id, get_hwid, get_mac_address};
28#[cfg(target_os = "windows")]
29use windows::{get_disk_id, get_hwid, get_mac_address};
30#[cfg(target_os = "macos")]
31use macos::{get_disk_id, get_hwid, get_mac_address};
32
33use hmac::{Hmac, Mac};
34use md5::Md5;
35use sha1::Sha1;
36use sha2::Sha256;
37use sysinfo::{CpuExt, System, SystemExt};
38use utils::file_token;
39
40/// The components that can be used to build the HWID.
41
42#[derive(PartialEq, Eq, Hash)]
43pub enum HWIDComponent {
44    /// System UUID
45    SystemID,
46    /// Number of CPU Cores
47    CPUCores,
48    /// Name of the OS
49    OSName,
50    /// Current Username
51    Username,
52    /// Host machine name
53    MachineName,
54    /// Mac Address
55    MacAddress,
56    /// CPU Vendor ID
57    CPUID,
58    /// The contents of a file
59    FileToken(&'static str),
60    /// UUID of the root disk
61    DriveSerial,
62}
63
64impl HWIDComponent {
65    fn to_string(&self) -> Result<String, HWIDError> {
66        use HWIDComponent::*;
67        return match self {
68            SystemID => get_hwid(),
69            CPUCores => {
70                let sys = System::new_all();
71                let cores = sys.physical_core_count().unwrap_or(2);
72                Ok(cores.to_string())
73            }
74            OSName => {
75                let sys = System::new_all();
76                let name = sys
77                    .long_os_version()
78                    .ok_or(HWIDError::new("OSName", "Could not retrieve OS Name"))?;
79                Ok(name)
80            }
81            Username => Ok(whoami::username()),
82            MachineName => {
83                let sys = System::new_all();
84                let name = sys
85                    .host_name()
86                    .ok_or(HWIDError::new("HostName", "Could not retrieve Host Name"))?;
87                Ok(name)
88            }
89            MacAddress => get_mac_address(),
90            CPUID => {
91                let sys = System::new_all();
92                let processor = sys.global_cpu_info();
93                Ok(processor.vendor_id().to_string())
94            }
95            FileToken(filename) => file_token(filename),
96            DriveSerial => get_disk_id(),
97        };
98    }
99}
100
101/// The encryptions that can be used to build the HWID.
102pub enum Encryption {
103    MD5,
104    SHA256,
105    SHA1,
106}
107
108type HmacMd5 = Hmac<Md5>;
109type HmacSha1 = Hmac<Sha1>;
110type HmacSha256 = Hmac<Sha256>;
111
112impl Encryption {
113    fn generate_hash(&self, key: &[u8], text: String) -> Result<String, HWIDError> {
114        match self {
115            Encryption::MD5 => {
116                let mut mac = HmacMd5::new_from_slice(key)?;
117                mac.update(text.as_bytes());
118                let result = mac.finalize();
119                Ok(hex::encode(result.into_bytes().as_slice()))
120            }
121            Encryption::SHA1 => {
122                let mut mac = HmacSha1::new_from_slice(key)?;
123                mac.update(text.as_bytes());
124                let result = mac.finalize();
125                Ok(hex::encode(result.into_bytes().as_slice()))
126            }
127            Encryption::SHA256 => {
128                let mut mac = HmacSha256::new_from_slice(key)?;
129                mac.update(text.as_bytes());
130                let result = mac.finalize();
131                Ok(hex::encode(result.into_bytes().as_slice()))
132            }
133        }
134    }
135}
136
137/// `IdBuilder` is the constructor for the HWID. It can be used with the 3 different options of the `Encryption` enum.
138pub struct IdBuilder {
139    parts: Vec<HWIDComponent>,
140    pub hash: Encryption,
141}
142
143impl IdBuilder {
144    /// Joins every part together and returns a `Result` that may be the hashed HWID or a `HWIDError`.
145    ///
146    /// # Errors
147    ///
148    /// Returns [`Err`] if there is an error while retrieving the component's strings.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use machineid_rs::{IdBuilder, Encryption, HWIDComponent};
154    ///
155    /// let mut builder = IdBuilder::new(Encryption::MD5);
156    ///
157    /// builder.add_component(HWIDComponent::SystemID);
158    ///
159    ///
160    /// // Will panic if there is an error when the components return his values.
161    /// let key = builder.build("mykey").unwrap();
162    /// ```
163    pub fn build(&mut self, key: &str) -> Result<String, HWIDError> {
164        if self.parts.len() == 0 {
165            panic!("You must add at least one element to make a machine id");
166        }
167        let final_string = self
168            .parts
169            .iter()
170            .map(|p| p.to_string())
171            .collect::<Result<String, HWIDError>>()?;
172        self.hash.generate_hash(key.as_bytes(), final_string)
173    }
174
175    /// Adds a component to the `IdBuilder` that will be hashed once you call the [`IdBuilder::build`] function.
176    ///
177    /// You can't add the same component twice.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// use machineid_rs::{IdBuilder, Encryption, HWIDComponent};
183    ///
184    /// let mut builder = IdBuilder::new(Encryption::MD5);
185    ///
186    /// builder.add_component(HWIDComponent::SystemID);
187    /// ```
188    pub fn add_component(&mut self, component: HWIDComponent) -> &mut Self {
189        if !self.parts.contains(&component) {
190            self.parts.push(component);
191        }
192        return self;
193    }
194
195    /// Makes a new IdBuilder with the selected Encryption
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use machineid_rs::{IdBuilder, Encryption};
201    ///
202    /// let mut builder = IdBuilder::new(Encryption::MD5);
203    /// ```
204    pub fn new(hash: Encryption) -> Self {
205        IdBuilder {
206            parts: vec![],
207            hash,
208        }
209    }
210}
211
212#[cfg(test)]
213mod test {
214    use super::*;
215    use std::env;
216    #[test]
217    fn every_option_sha256() {
218        let mut builder = IdBuilder::new(Encryption::SHA256);
219        builder
220            .add_component(HWIDComponent::SystemID)
221            .add_component(HWIDComponent::OSName)
222            .add_component(HWIDComponent::CPUCores)
223            .add_component(HWIDComponent::CPUID)
224            .add_component(HWIDComponent::DriveSerial)
225            .add_component(HWIDComponent::MacAddress)
226            .add_component(HWIDComponent::FileToken("test.txt"))
227            .add_component(HWIDComponent::Username)
228            .add_component(HWIDComponent::MachineName);
229        let hash = builder.build("mykey").unwrap();
230        let expected = env::var("SHA256_MACHINEID_HASH").unwrap();
231        assert_eq!(expected, hash);
232    }
233
234    #[test]
235    fn every_option_sha1() {
236        let mut builder = IdBuilder::new(Encryption::SHA1);
237        builder
238            .add_component(HWIDComponent::SystemID)
239            .add_component(HWIDComponent::OSName)
240            .add_component(HWIDComponent::CPUCores)
241            .add_component(HWIDComponent::CPUID)
242            .add_component(HWIDComponent::DriveSerial)
243            .add_component(HWIDComponent::MacAddress)
244            .add_component(HWIDComponent::FileToken("test.txt"))
245            .add_component(HWIDComponent::Username)
246            .add_component(HWIDComponent::MachineName);
247        let hash = builder.build("mykey").unwrap();
248        let expected = env::var("SHA1_MACHINEID_HASH").unwrap();
249        assert_eq!(expected, hash);
250    }
251
252    #[test]
253    fn every_option_md5() {
254        let mut builder = IdBuilder::new(Encryption::MD5);
255        builder
256            .add_component(HWIDComponent::SystemID)
257            .add_component(HWIDComponent::OSName)
258            .add_component(HWIDComponent::CPUCores)
259            .add_component(HWIDComponent::CPUID)
260            .add_component(HWIDComponent::DriveSerial)
261            .add_component(HWIDComponent::MacAddress)
262            .add_component(HWIDComponent::FileToken("test.txt"))
263            .add_component(HWIDComponent::Username)
264            .add_component(HWIDComponent::MachineName);
265        let hash = builder.build("mykey").unwrap();
266        let expected = env::var("MD5_MACHINEID_HASH").unwrap();
267        assert_eq!(expected, hash);
268    }
269}