1#![allow(non_snake_case)]
18
19mod errors;
20#[cfg(target_os = "linux")]
21mod linux;
22#[cfg(target_os = "macos")]
23mod macos;
24#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
25mod unsupported;
26mod utils;
27#[cfg(target_os = "windows")]
28mod windows;
29
30use errors::HWIDError;
31#[cfg(target_os = "linux")]
32use linux::{get_disk_id, get_hwid, get_mac_address};
33#[cfg(target_os = "macos")]
34use macos::{get_disk_id, get_hwid, get_mac_address};
35#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
36use unsupported::{get_disk_id, get_hwid, get_mac_address};
37#[cfg(target_os = "windows")]
38use windows::{get_disk_id, get_hwid, get_mac_address};
39
40use hmac::{Hmac, Mac};
41use md5::{Digest as Md5Digest, Md5};
42#[allow(unused_imports)]
43use sha1::{Digest as Sha1Digest, Sha1};
44#[allow(unused_imports)]
45use sha2::{Digest as Sha256Digest, Sha256};
46use sysinfo::{CpuRefreshKind, RefreshKind, System};
47use utils::file_token;
48
49#[derive(Debug, PartialEq, Eq, Hash)]
52pub enum HWIDComponent {
53 SystemID,
55 CPUCores,
57 OSName,
59 Username,
61 MachineName,
63 MacAddress,
65 CPUID,
67 FileToken(&'static str),
69 DriveSerial,
71}
72
73impl HWIDComponent {
74 pub fn to_string(&self) -> Result<String, HWIDError> {
75 use HWIDComponent::*;
76
77 match self {
78 SystemID => get_hwid(),
79 CPUCores => {
80 let sys = System::new_with_specifics(
81 RefreshKind::nothing().with_cpu(CpuRefreshKind::nothing()),
82 );
83 let cores = sys
84 .physical_core_count()
85 .ok_or(HWIDError::new("CPUCores", "Could not retrieve CPU Cores"))?;
86 Ok(cores.to_string())
87 }
88 OSName => {
89 let name = System::long_os_version()
90 .ok_or(HWIDError::new("OSName", "Could not retrieve OS Name"))?;
91 Ok(name)
92 }
93 Username => Ok(whoami::username()),
94 MachineName => {
95 let name = System::host_name()
96 .ok_or(HWIDError::new("HostName", "Could not retrieve Host Name"))?;
97 Ok(name)
98 }
99 MacAddress => get_mac_address(),
100 CPUID => {
101 let sys = System::new_with_specifics(
102 RefreshKind::nothing().with_cpu(CpuRefreshKind::nothing()),
103 );
104 let processor = sys
105 .cpus()
106 .first()
107 .ok_or(HWIDError::new("CPUID", "Could not retrieve CPU ID"))?;
108 Ok(processor.vendor_id().to_string())
109 }
110 FileToken(filename) => file_token(filename),
111 DriveSerial => get_disk_id(),
112 }
113 }
114}
115
116pub enum Encryption {
118 MD5,
119 SHA256,
120 SHA1,
121}
122
123type HmacMd5 = Hmac<Md5>;
124type HmacSha1 = Hmac<Sha1>;
125type HmacSha256 = Hmac<Sha256>;
126
127impl Encryption {
128 fn generate_hash(&self, key: Option<&[u8]>, text: String) -> Result<String, HWIDError> {
129 match self {
130 Encryption::MD5 => {
131 if key.is_none() {
132 let mut hasher = Md5::new();
133 hasher.update(text.as_bytes());
134 Ok(hex::encode(hasher.finalize()))
135 } else {
136 let mut mac = HmacMd5::new_from_slice(key.unwrap())?;
137 mac.update(text.as_bytes());
138 let result = mac.finalize();
139 Ok(hex::encode(result.into_bytes().as_slice()))
140 }
141 }
142 Encryption::SHA1 => {
143 if key.is_none() {
144 let mut hasher = Sha1::new();
145 hasher.update(text.as_bytes());
146 Ok(hex::encode(hasher.finalize()))
147 } else {
148 let mut mac = HmacSha1::new_from_slice(key.unwrap())?;
149 mac.update(text.as_bytes());
150 let result = mac.finalize();
151 Ok(hex::encode(result.into_bytes().as_slice()))
152 }
153 }
154 Encryption::SHA256 => {
155 if key.is_none() {
156 let mut hasher = Sha256::new();
157 hasher.update(text.as_bytes());
158 Ok(hex::encode(hasher.finalize()))
159 } else {
160 let mut mac = HmacSha256::new_from_slice(key.unwrap())?;
161 mac.update(text.as_bytes());
162 let result = mac.finalize();
163 Ok(hex::encode(result.into_bytes().as_slice()))
164 }
165 }
166 }
167 }
168}
169
170pub struct IdBuilder {
172 parts: Vec<HWIDComponent>,
173 pub hash: Encryption,
174}
175
176impl IdBuilder {
177 pub fn build(&mut self, key: Option<&str>) -> Result<String, HWIDError> {
197 if self.parts.is_empty() {
198 panic!("You must add at least one element to make a machine id");
199 }
200 let final_string = self
201 .parts
202 .iter()
203 .map(|p| p.to_string())
204 .collect::<Result<String, HWIDError>>()?;
205
206 self.hash
207 .generate_hash(key.map(|k| k.as_bytes()), final_string)
208 }
209
210 pub fn add_component(&mut self, component: HWIDComponent) -> &mut Self {
224 if !self.parts.contains(&component) {
225 self.parts.push(component);
226 }
227 self
228 }
229
230 pub fn add_all(&mut self) -> &mut Self {
261 self.add_component(HWIDComponent::SystemID)
262 .add_component(HWIDComponent::OSName)
263 .add_component(HWIDComponent::CPUCores)
264 .add_component(HWIDComponent::CPUID)
265 .add_component(HWIDComponent::DriveSerial)
266 .add_component(HWIDComponent::MacAddress)
267 .add_component(HWIDComponent::Username)
268 .add_component(HWIDComponent::MachineName)
269 }
270
271 pub fn new(hash: Encryption) -> Self {
281 IdBuilder {
282 parts: vec![],
283 hash,
284 }
285 }
286}
287
288#[cfg(test)]
289mod test {
290 use super::*;
291 use std::env;
292
293 #[test]
294 fn every_option_sha256() {
295 let mut builder = IdBuilder::new(Encryption::SHA256);
296 builder
297 .add_component(HWIDComponent::SystemID)
298 .add_component(HWIDComponent::OSName)
299 .add_component(HWIDComponent::CPUCores)
300 .add_component(HWIDComponent::CPUID)
301 .add_component(HWIDComponent::DriveSerial)
302 .add_component(HWIDComponent::MacAddress)
303 .add_component(HWIDComponent::FileToken("test.txt"))
304 .add_component(HWIDComponent::Username)
305 .add_component(HWIDComponent::MachineName);
306 let hash = builder.build(None).unwrap();
307 if let Ok(expected) = env::var("SHA256_MACHINEID_HASH") {
308 assert_eq!(expected, hash);
309 }
310 }
311
312 #[test]
313 fn every_option_sha1() {
314 let mut builder = IdBuilder::new(Encryption::SHA1);
315 builder
316 .add_component(HWIDComponent::SystemID)
317 .add_component(HWIDComponent::OSName)
318 .add_component(HWIDComponent::CPUCores)
319 .add_component(HWIDComponent::CPUID)
320 .add_component(HWIDComponent::DriveSerial)
321 .add_component(HWIDComponent::MacAddress)
322 .add_component(HWIDComponent::FileToken("test.txt"))
323 .add_component(HWIDComponent::Username)
324 .add_component(HWIDComponent::MachineName);
325 let hash = builder.build(None).unwrap();
326 if let Ok(expected) = env::var("SHA1_MACHINEID_HASH") {
327 assert_eq!(expected, hash);
328 }
329 }
330
331 #[test]
332 fn every_option_md5() {
333 let mut builder = IdBuilder::new(Encryption::MD5);
334 builder
335 .add_component(HWIDComponent::SystemID)
336 .add_component(HWIDComponent::OSName)
337 .add_component(HWIDComponent::CPUCores)
338 .add_component(HWIDComponent::CPUID)
339 .add_component(HWIDComponent::DriveSerial)
340 .add_component(HWIDComponent::MacAddress)
341 .add_component(HWIDComponent::FileToken("test.txt"))
342 .add_component(HWIDComponent::Username)
343 .add_component(HWIDComponent::MachineName);
344 let hash = builder.build(None).unwrap();
345 if let Ok(expected) = env::var("MD5_MACHINEID_HASH") {
346 assert_eq!(expected, hash);
347 }
348 }
349
350 #[test]
351 fn every_option_sha256_hmac() {
352 let mut builder = IdBuilder::new(Encryption::SHA256);
353 builder
354 .add_component(HWIDComponent::SystemID)
355 .add_component(HWIDComponent::OSName)
356 .add_component(HWIDComponent::CPUCores)
357 .add_component(HWIDComponent::CPUID)
358 .add_component(HWIDComponent::DriveSerial)
359 .add_component(HWIDComponent::MacAddress)
360 .add_component(HWIDComponent::FileToken("test.txt"))
361 .add_component(HWIDComponent::Username)
362 .add_component(HWIDComponent::MachineName);
363 let hash = builder.build(Some("mykey")).unwrap();
364 if let Ok(expected) = env::var("SHA256_MACHINEID_HASH") {
365 assert_eq!(expected, hash);
366 }
367 }
368
369 #[test]
370 fn every_option_sha1_hmac() {
371 let mut builder = IdBuilder::new(Encryption::SHA1);
372 builder
373 .add_component(HWIDComponent::SystemID)
374 .add_component(HWIDComponent::OSName)
375 .add_component(HWIDComponent::CPUCores)
376 .add_component(HWIDComponent::CPUID)
377 .add_component(HWIDComponent::DriveSerial)
378 .add_component(HWIDComponent::MacAddress)
379 .add_component(HWIDComponent::FileToken("test.txt"))
380 .add_component(HWIDComponent::Username)
381 .add_component(HWIDComponent::MachineName);
382 let hash = builder.build(Some("mykey")).unwrap();
383 if let Ok(expected) = env::var("SHA1_MACHINEID_HASH") {
384 assert_eq!(expected, hash);
385 }
386 }
387
388 #[test]
389 fn every_option_md5_hmac() {
390 let mut builder = IdBuilder::new(Encryption::MD5);
391 builder
392 .add_component(HWIDComponent::SystemID)
393 .add_component(HWIDComponent::OSName)
394 .add_component(HWIDComponent::CPUCores)
395 .add_component(HWIDComponent::CPUID)
396 .add_component(HWIDComponent::DriveSerial)
397 .add_component(HWIDComponent::MacAddress)
398 .add_component(HWIDComponent::Username)
400 .add_component(HWIDComponent::MachineName);
401 let hash = builder.build(Some("mykey")).unwrap();
402 if let Ok(expected) = env::var("MD5_MACHINEID_HASH") {
403 assert_eq!(expected, hash);
404 }
405 }
406}