ferogram_mtproto/
bind_temp_key.rs1use ferogram_crypto::{aes, derive_aes_key_iv_v1};
14use sha1::{Digest, Sha1};
15
16fn serialize_inner(
17 nonce: i64,
18 temp_auth_key_id: i64,
19 perm_auth_key_id: i64,
20 temp_session_id: i64,
21 expires_at: i32,
22) -> [u8; 40] {
23 let mut out = [0u8; 40];
24 out[0..4].copy_from_slice(&0x75a3f765_u32.to_le_bytes());
25 out[4..12].copy_from_slice(&nonce.to_le_bytes());
26 out[12..20].copy_from_slice(&temp_auth_key_id.to_le_bytes());
27 out[20..28].copy_from_slice(&perm_auth_key_id.to_le_bytes());
28 out[28..36].copy_from_slice(&temp_session_id.to_le_bytes());
29 out[36..40].copy_from_slice(&expires_at.to_le_bytes());
30 out
31}
32
33pub fn encrypt_bind_inner(
35 perm_auth_key: &[u8; 256],
36 msg_id: i64,
37 nonce: i64,
38 temp_auth_key_id: i64,
39 perm_auth_key_id: i64,
40 temp_session_id: i64,
41 expires_at: i32,
42) -> Vec<u8> {
43 let inner = serialize_inner(
44 nonce,
45 temp_auth_key_id,
46 perm_auth_key_id,
47 temp_session_id,
48 expires_at,
49 );
50
51 let header_len = 32usize;
52 let content_len = header_len + 40;
53 let pad_len = (16 - content_len % 16) % 16;
54 let total = content_len + pad_len;
55
56 let mut rnd = [0u8; 24];
57 getrandom::getrandom(&mut rnd).expect("getrandom");
58
59 let mut plaintext = Vec::with_capacity(total);
60 plaintext.extend_from_slice(&rnd[..8]);
61 plaintext.extend_from_slice(&rnd[8..16]);
62 plaintext.extend_from_slice(&msg_id.to_le_bytes());
63 plaintext.extend_from_slice(&0i32.to_le_bytes());
64 plaintext.extend_from_slice(&40u32.to_le_bytes());
65 plaintext.extend_from_slice(&inner);
66 plaintext.extend_from_slice(&rnd[16..16 + pad_len]);
67 assert_eq!(plaintext.len(), total);
68
69 let hash: [u8; 20] = {
70 let mut h = Sha1::new();
71 h.update(&plaintext[..content_len]);
72 h.finalize().into()
73 };
74 let mut msg_key = [0u8; 16];
75 msg_key.copy_from_slice(&hash[4..20]);
76
77 let (aes_key, aes_iv) = derive_aes_key_iv_v1(perm_auth_key, &msg_key);
78 aes::ige_encrypt(&mut plaintext, &aes_key, &aes_iv);
79
80 let key_sha: [u8; 20] = {
81 let mut h = Sha1::new();
82 h.update(perm_auth_key);
83 h.finalize().into()
84 };
85
86 let mut result = Vec::with_capacity(8 + 16 + plaintext.len());
87 result.extend_from_slice(&key_sha[12..20]);
88 result.extend_from_slice(&msg_key);
89 result.extend_from_slice(&plaintext);
90 result
91}
92
93pub fn serialize_bind_temp_auth_key(
95 perm_auth_key_id: i64,
96 nonce: i64,
97 expires_at: i32,
98 encrypted_message: &[u8],
99) -> Vec<u8> {
100 let mut out = Vec::new();
101 out.extend_from_slice(&0xcdd42a05_u32.to_le_bytes());
102 out.extend_from_slice(&perm_auth_key_id.to_le_bytes());
103 out.extend_from_slice(&nonce.to_le_bytes());
104 out.extend_from_slice(&expires_at.to_le_bytes());
105 tl_write_bytes(&mut out, encrypted_message);
106 out
107}
108
109pub fn auth_key_id_from_key(key: &[u8; 256]) -> i64 {
111 use sha1::{Digest, Sha1};
112 let hash: [u8; 20] = Sha1::new().chain_update(key).finalize().into();
113 i64::from_le_bytes(hash[12..20].try_into().unwrap())
114}
115
116pub fn gen_msg_id() -> i64 {
126 use std::time::{SystemTime, UNIX_EPOCH};
127 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
128 let secs = now.as_secs();
129 let nanos = now.subsec_nanos() as u64;
130 ((secs << 32) | (nanos << 2)) as i64
133}
134
135fn tl_write_bytes(out: &mut Vec<u8>, data: &[u8]) {
136 let len = data.len();
137 if len < 254 {
138 out.push(len as u8);
139 out.extend_from_slice(data);
140 let pad = (4 - (1 + len) % 4) % 4;
141 out.extend(std::iter::repeat_n(0u8, pad));
142 } else {
143 out.push(0xfe);
144 out.push((len & 0xff) as u8);
145 out.push(((len >> 8) & 0xff) as u8);
146 out.push(((len >> 16) & 0xff) as u8);
147 out.extend_from_slice(data);
148 let pad = (4 - (4 + len) % 4) % 4;
149 out.extend(std::iter::repeat_n(0u8, pad));
150 }
151}