steam_machine_id/
lib.rs

1#![warn(missing_docs)]
2
3//! Used for generating Steam machine IDs. Machine IDs are most commonly supplied to Steam when 
4//! logging in.
5//! 
6//! # Usage
7//! 
8//! Generating random machine IDs.
9//! ```
10//! use steam_machine_id::MachineID;
11//! 
12//! // Creates a random machine ID.
13//! let machine_id = MachineID::random();
14//! ```
15//! 
16//! Consuming a generated machine ID for a login request.
17//! ```
18//! use steam_machine_id::MachineID;
19//! 
20//! struct LoginRequest {
21//!     machine_id: Vec<u8>,
22//! }
23//! 
24//! // Creates a machine ID from the given account name.
25//! let machine_id = MachineID::from_account_name("accountname");
26//! let login = LoginRequest {
27//!     // Converts the machine ID into a binary message object.
28//!     machine_id: machine_id.into(),
29//! };
30//! ```
31
32mod helpers;
33
34use std::fmt;
35use helpers::Sha1HashValue;
36
37/// A Steam machine ID.
38#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39pub struct MachineID {
40    /// The BB3 SHA1 hash value. This value is not hexadecimal encoded.
41    pub value_bb3: Sha1HashValue,
42    /// The FF2 SHA1 hash value. This value is not hexadecimal encoded.
43    pub value_ff2: Sha1HashValue,
44    /// The 3B3 SHA1 hash value. This value is not hexadecimal encoded.
45    pub value_3b3: Sha1HashValue,
46}
47
48impl MachineID {
49    /// Creates a random machine ID.
50    /// 
51    /// # Examples
52    /// ```
53    /// use steam_machine_id::MachineID;
54    /// 
55    /// let machine_id = MachineID::random();
56    /// ```
57    pub fn random() -> Self {
58        Self {
59            value_bb3: helpers::get_random_hash_value(),
60            value_ff2: helpers::get_random_hash_value(),
61            value_3b3: helpers::get_random_hash_value(),
62        }
63    }
64    
65    /// Creates a machine ID from the given account name.
66    /// 
67    /// # Examples
68    /// ```
69    /// use steam_machine_id::MachineID;
70    /// 
71    /// let machine_id = MachineID::from_account_name("accountname");
72    /// ```
73    pub fn from_account_name<S: AsRef<str>>(account_name: S) -> Self {
74        Self {
75            value_bb3: helpers::get_account_name_hash_value("BB3", account_name.as_ref()),
76            value_ff2: helpers::get_account_name_hash_value("FF2", account_name.as_ref()),
77            value_3b3: helpers::get_account_name_hash_value("3B3", account_name.as_ref()),
78        }
79    }
80    
81    /// Creates a machine ID using a custom format for specific use-cases. These could be anything 
82    /// you want but should generally follow the format below.
83    /// 
84    /// # Examples
85    /// ```
86    /// use steam_machine_id::MachineID;
87    /// 
88    /// let accountname = "accountname";
89    /// let machine_id = MachineID::custom_format(
90    ///     &format!("SteamUser Hash BB3 {accountname}"),
91    ///     &format!("SteamUser Hash FF2 {accountname}"),
92    ///     &format!("SteamUser Hash 3B3 {accountname}"),
93    /// );
94    /// ```
95    pub fn custom_format<S: AsRef<str>>(
96        value_bb3: S,
97        value_ff2: S,
98        value_3b3: S,
99    ) -> Self {
100        Self {
101            value_bb3: helpers::get_custom_hash_value(value_bb3.as_ref()),
102            value_ff2: helpers::get_custom_hash_value(value_ff2.as_ref()),
103            value_3b3: helpers::get_custom_hash_value(value_3b3.as_ref()),
104        }
105    }
106    
107    /// Creates a message object from the machine ID.
108    pub fn to_message(&self) -> Vec<u8> {
109        helpers::create_machine_id_from_values(
110            &self.value_bb3,
111            &self.value_ff2,
112            &self.value_3b3,
113        )
114    }
115}
116
117impl From<MachineID> for Vec<u8> {
118    fn from(machine_id: MachineID) -> Self {
119        machine_id.to_message()
120    }
121}
122
123impl From<&MachineID> for Vec<u8> {
124    fn from(machine_id: &MachineID) -> Self {
125        machine_id.to_message()
126    }
127}
128
129impl fmt::Display for MachineID {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        write!(
132            f,
133            "BB3.{}:FF2.{}:3B3.{}", 
134            helpers::bytes_to_hex_string(&self.value_bb3),
135            helpers::bytes_to_hex_string(&self.value_ff2),
136            helpers::bytes_to_hex_string(&self.value_3b3),
137        )
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use helpers::{get_c_string, bytes_to_hex_string};
145    
146    #[test]
147    fn test_random_machine_id() {
148        let machine_id = MachineID::random().to_message();
149        
150        assert_eq!(machine_id.len(), 155);
151        assert_eq!(machine_id[0], 0);
152        assert_eq!(&machine_id[1..15], get_c_string("MessageObject").as_slice());
153        assert_eq!(machine_id[15], 1);
154        assert_eq!(&machine_id[16..20], get_c_string("BB3").as_slice());
155        assert_eq!(machine_id[61], 1);
156        assert_eq!(&machine_id[62..66], get_c_string("FF2").as_slice());
157        assert_eq!(machine_id[107], 1);
158        assert_eq!(&machine_id[108..112], get_c_string("3B3").as_slice());
159        assert_eq!(machine_id[153], 8);
160        assert_eq!(machine_id[154], 8);
161    }
162    
163    #[test]
164    fn test_create_machine_id_from_account_name() {
165        let machine_id = MachineID::from_account_name("accountname").to_message();
166        
167        assert_eq!(machine_id.len(), 155);
168        assert_eq!(machine_id[0], 0);
169        assert_eq!(&machine_id[1..15], get_c_string("MessageObject").as_slice());
170        assert_eq!(machine_id[15], 1);
171        assert_eq!(&machine_id[16..20], get_c_string("BB3").as_slice());
172        assert_eq!(machine_id[61], 1);
173        assert_eq!(&machine_id[62..66], get_c_string("FF2").as_slice());
174        assert_eq!(machine_id[107], 1);
175        assert_eq!(&machine_id[108..112], get_c_string("3B3").as_slice());
176        assert_eq!(machine_id[153], 8);
177        assert_eq!(machine_id[154], 8);
178    }
179    
180    #[test]
181    fn tests_machine_id() {
182        let machine_id = MachineID::from_account_name("accountname");
183        
184        assert_eq!(bytes_to_hex_string(&machine_id.value_bb3), "6BB2445F8825BFED65E64392F0A4D549FFF7D3E1");
185        assert_eq!(bytes_to_hex_string(&machine_id.value_ff2), "57AD645E54976AFF3B3662E9CB335D0A24AC7D08");
186        assert_eq!(bytes_to_hex_string(&machine_id.value_3b3), "C1884025D23FB1A0DDBF125B5D9B8C0812F83390");
187    }
188}