Skip to main content

ferogram_mtproto/
bind_temp_key.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3//
4// ferogram: async Telegram MTProto client in Rust
5// https://github.com/ankit-chaubey/ferogram
6//
7// If you use or modify this code, keep this notice at the top of your file
8// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
9// https://github.com/ankit-chaubey/ferogram
10
11use ferogram_crypto::{aes, derive_aes_key_iv_v1};
12use sha1::{Digest, Sha1};
13
14fn serialize_inner(
15    nonce: i64,
16    temp_auth_key_id: i64,
17    perm_auth_key_id: i64,
18    temp_session_id: i64,
19    expires_at: i32,
20) -> [u8; 40] {
21    let mut out = [0u8; 40];
22    out[0..4].copy_from_slice(&0x75a3f765_u32.to_le_bytes());
23    out[4..12].copy_from_slice(&nonce.to_le_bytes());
24    out[12..20].copy_from_slice(&temp_auth_key_id.to_le_bytes());
25    out[20..28].copy_from_slice(&perm_auth_key_id.to_le_bytes());
26    out[28..36].copy_from_slice(&temp_session_id.to_le_bytes());
27    out[36..40].copy_from_slice(&expires_at.to_le_bytes());
28    out
29}
30
31/// Build the `encrypted_message` bytes for `auth.bindTempAuthKey`.
32pub fn encrypt_bind_inner(
33    perm_auth_key: &[u8; 256],
34    msg_id: i64,
35    nonce: i64,
36    temp_auth_key_id: i64,
37    perm_auth_key_id: i64,
38    temp_session_id: i64,
39    expires_at: i32,
40) -> Vec<u8> {
41    let inner = serialize_inner(
42        nonce,
43        temp_auth_key_id,
44        perm_auth_key_id,
45        temp_session_id,
46        expires_at,
47    );
48
49    let header_len = 32usize;
50    let content_len = header_len + 40;
51    let pad_len = (16 - content_len % 16) % 16;
52    let total = content_len + pad_len;
53
54    let mut rnd = [0u8; 24];
55    getrandom::getrandom(&mut rnd).expect("getrandom");
56
57    let mut plaintext = Vec::with_capacity(total);
58    plaintext.extend_from_slice(&rnd[..8]);
59    plaintext.extend_from_slice(&rnd[8..16]);
60    plaintext.extend_from_slice(&msg_id.to_le_bytes());
61    plaintext.extend_from_slice(&0i32.to_le_bytes());
62    plaintext.extend_from_slice(&40u32.to_le_bytes());
63    plaintext.extend_from_slice(&inner);
64    plaintext.extend_from_slice(&rnd[16..16 + pad_len]);
65    assert_eq!(plaintext.len(), total);
66
67    let hash: [u8; 20] = {
68        let mut h = Sha1::new();
69        h.update(&plaintext[..content_len]);
70        h.finalize().into()
71    };
72    let mut msg_key = [0u8; 16];
73    msg_key.copy_from_slice(&hash[4..20]);
74
75    let (aes_key, aes_iv) = derive_aes_key_iv_v1(perm_auth_key, &msg_key);
76    aes::ige_encrypt(&mut plaintext, &aes_key, &aes_iv);
77
78    let key_sha: [u8; 20] = {
79        let mut h = Sha1::new();
80        h.update(perm_auth_key);
81        h.finalize().into()
82    };
83
84    let mut result = Vec::with_capacity(8 + 16 + plaintext.len());
85    result.extend_from_slice(&key_sha[12..20]);
86    result.extend_from_slice(&msg_key);
87    result.extend_from_slice(&plaintext);
88    result
89}
90
91/// Serialize `auth.bindTempAuthKey#cdd42a05` to raw TL bytes.
92pub fn serialize_bind_temp_auth_key(
93    perm_auth_key_id: i64,
94    nonce: i64,
95    expires_at: i32,
96    encrypted_message: &[u8],
97) -> Vec<u8> {
98    let mut out = Vec::new();
99    out.extend_from_slice(&0xcdd42a05_u32.to_le_bytes());
100    out.extend_from_slice(&perm_auth_key_id.to_le_bytes());
101    out.extend_from_slice(&nonce.to_le_bytes());
102    out.extend_from_slice(&expires_at.to_le_bytes());
103    tl_write_bytes(&mut out, encrypted_message);
104    out
105}
106
107/// Generate a monotonic MTProto message ID from the current system clock.
108pub fn gen_msg_id() -> i64 {
109    use std::time::{SystemTime, UNIX_EPOCH};
110    let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
111    ((now.as_secs() << 32) | (now.subsec_nanos() as u64 & !3)) as i64
112}
113
114fn tl_write_bytes(out: &mut Vec<u8>, data: &[u8]) {
115    let len = data.len();
116    if len < 254 {
117        out.push(len as u8);
118        out.extend_from_slice(data);
119        let pad = (4 - (1 + len) % 4) % 4;
120        out.extend(std::iter::repeat_n(0u8, pad));
121    } else {
122        out.push(0xfe);
123        out.push((len & 0xff) as u8);
124        out.push(((len >> 8) & 0xff) as u8);
125        out.push(((len >> 16) & 0xff) as u8);
126        out.extend_from_slice(data);
127        let pad = (4 - (4 + len) % 4) % 4;
128        out.extend(std::iter::repeat_n(0u8, pad));
129    }
130}