Skip to main content

hermes_tdata/
account.rs

1//! Account representation
2
3use std::fmt;
4use std::net::Ipv4Addr;
5
6use crate::AUTH_KEY_SIZE;
7
8/// Telegram datacenter addresses (production)
9const DC_ADDRESSES: [(i32, Ipv4Addr, u16); 5] = [
10    (1, Ipv4Addr::new(149, 154, 175, 53), 443),
11    (2, Ipv4Addr::new(149, 154, 167, 51), 443),
12    (3, Ipv4Addr::new(149, 154, 175, 100), 443),
13    (4, Ipv4Addr::new(149, 154, 167, 91), 443),
14    (5, Ipv4Addr::new(91, 108, 56, 130), 443),
15];
16
17/// A Telegram account extracted from tdata
18pub struct Account {
19    /// Account index (0-2)
20    index: i32,
21    /// Datacenter ID (1-5)
22    dc_id: i32,
23    /// User ID
24    user_id: i64,
25    /// Authorization key (256 bytes)
26    auth_key: [u8; AUTH_KEY_SIZE],
27}
28
29impl fmt::Debug for Account {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_struct("Account")
32            .field("index", &self.index)
33            .field("dc_id", &self.dc_id)
34            .field("user_id", &"<redacted>")
35            .field("auth_key", &"<redacted>")
36            .finish()
37    }
38}
39
40impl Account {
41    /// Create a new account
42    pub(crate) fn new(index: i32, dc_id: i32, user_id: i64, auth_key: [u8; AUTH_KEY_SIZE]) -> Self {
43        Self {
44            index,
45            dc_id,
46            user_id,
47            auth_key,
48        }
49    }
50
51    /// Get the account index (0-2)
52    pub fn index(&self) -> i32 {
53        self.index
54    }
55
56    /// Get the datacenter ID (1-5)
57    pub fn dc_id(&self) -> i32 {
58        self.dc_id
59    }
60
61    /// Get the Telegram user ID.
62    ///
63    /// Treat account identifiers as private data and avoid logging them.
64    pub fn user_id(&self) -> i64 {
65        self.user_id
66    }
67
68    /// Get the raw auth key bytes.
69    ///
70    /// This grants access to the Telegram account. Never log, serialize, or expose it.
71    pub fn auth_key_bytes(&self) -> &[u8; AUTH_KEY_SIZE] {
72        &self.auth_key
73    }
74
75    /// Convert to grammers SessionData.
76    ///
77    /// The returned value contains live authentication credentials and can be imported
78    /// into any `grammers` session storage. Never log or expose it.
79    pub fn to_grammers_session_data(&self) -> grammers_session::SessionData {
80        use grammers_session::{types::DcOption, SessionData};
81        use std::net::{SocketAddrV4, SocketAddrV6};
82
83        // Get or create DC option with auth key
84        let (ip, port) = DC_ADDRESSES
85            .iter()
86            .find(|(id, _, _)| *id == self.dc_id)
87            .map(|(_, ip, port)| (*ip, *port))
88            .unwrap_or((Ipv4Addr::new(149, 154, 167, 51), 443));
89
90        let ipv4 = ip;
91        let ipv6 = ipv4.to_ipv6_mapped();
92
93        let mut session_data = SessionData {
94            home_dc: self.dc_id,
95            ..SessionData::default()
96        };
97
98        // Update the DC option with our auth key
99        if let Some(dc_option) = session_data.dc_options.get_mut(&self.dc_id) {
100            dc_option.auth_key = Some(self.auth_key);
101        } else {
102            session_data.dc_options.insert(
103                self.dc_id,
104                DcOption {
105                    id: self.dc_id,
106                    ipv4: SocketAddrV4::new(ipv4, port),
107                    ipv6: SocketAddrV6::new(ipv6, port, 0, 0),
108                    auth_key: Some(self.auth_key),
109                },
110            );
111        }
112
113        session_data
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_account_creation() {
123        let auth_key = [0xAB; AUTH_KEY_SIZE];
124        let account = Account::new(0, 2, 12345678, auth_key);
125
126        assert_eq!(account.index(), 0);
127        assert_eq!(account.dc_id(), 2);
128        assert_eq!(account.user_id(), 12345678);
129        assert_eq!(account.auth_key_bytes(), &auth_key);
130    }
131
132    #[test]
133    fn debug_redacts_account_identity_and_auth_key() {
134        let account = Account::new(0, 2, 12_345_678, [0xAB; AUTH_KEY_SIZE]);
135        let debug = format!("{account:?}");
136
137        assert!(debug.contains("<redacted>"));
138        assert!(!debug.contains("12345678"));
139        assert!(!debug.contains("171, 171"));
140    }
141}