1use sha1::{Digest as Sha1Digest, Sha1};
9use sha2::Sha512;
10
11use crate::{Error, Result, AUTH_KEY_SIZE};
12
13pub const LOCAL_ENCRYPT_SALT_SIZE: usize = 32;
15
16pub const AES_KEY_SIZE: usize = 32;
18
19pub const AES_BLOCK_SIZE: usize = 16;
21
22const PBKDF2_ITERATIONS_WITH_PASSCODE: u32 = 100_000;
24
25const PBKDF2_ITERATIONS_NO_PASSCODE: u32 = 1;
27
28#[derive(Clone)]
30pub struct AuthKey {
31 data: [u8; AUTH_KEY_SIZE],
32}
33
34impl AuthKey {
35 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
37 if bytes.len() != AUTH_KEY_SIZE {
38 return Err(Error::invalid_format(format!(
39 "auth key must be {} bytes, got {}",
40 AUTH_KEY_SIZE,
41 bytes.len()
42 )));
43 }
44
45 let mut data = [0u8; AUTH_KEY_SIZE];
46 data.copy_from_slice(bytes);
47 Ok(Self { data })
48 }
49
50 pub fn as_bytes(&self) -> &[u8; AUTH_KEY_SIZE] {
54 &self.data
55 }
56}
57
58impl std::fmt::Debug for AuthKey {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.debug_struct("AuthKey")
62 .field("len", &self.data.len())
63 .finish()
64 }
65}
66
67pub fn create_local_key(salt: &[u8], passcode: &[u8]) -> AuthKey {
74 let mut key_data = [0u8; AUTH_KEY_SIZE];
75
76 let mut hasher = Sha512::new();
78 hasher.update(salt);
79 hasher.update(passcode);
80 hasher.update(salt);
81 let hash_key = hasher.finalize();
82
83 let iterations = if passcode.is_empty() {
85 PBKDF2_ITERATIONS_NO_PASSCODE
86 } else {
87 PBKDF2_ITERATIONS_WITH_PASSCODE
88 };
89
90 pbkdf2::pbkdf2_hmac::<Sha512>(&hash_key, salt, iterations, &mut key_data);
92
93 AuthKey { data: key_data }
94}
95
96pub fn decrypt_local(encrypted: &[u8], key: &AuthKey) -> Result<Vec<u8>> {
107 if encrypted.len() <= AES_BLOCK_SIZE {
108 return Err(Error::invalid_format("encrypted data too short"));
109 }
110
111 if encrypted.len() % AES_BLOCK_SIZE != 0 {
112 return Err(Error::invalid_format(
113 "encrypted data length must be multiple of 16",
114 ));
115 }
116
117 let (encrypted_key, encrypted_data) = encrypted.split_at(AES_BLOCK_SIZE);
119 let encrypted_key: &[u8; AES_BLOCK_SIZE] = encrypted_key
120 .try_into()
121 .map_err(|_| Error::invalid_format("invalid encrypted message key"))?;
122
123 tracing::debug!("decrypt_local: encrypted len={}", encrypted.len());
124
125 let (aes_key, aes_iv) = prepare_aes_oldmtp(key.as_bytes(), encrypted_key)?;
127
128 let decrypted = ige_decrypt(&aes_key, &aes_iv, encrypted_data);
130
131 let digest = sha1_hash(&decrypted);
133 let (check_hash, _) = digest.split_at(AES_BLOCK_SIZE);
134
135 tracing::debug!("Computed decrypted payload integrity check");
136
137 if check_hash != encrypted_key {
138 return Err(Error::ChecksumMismatch);
139 }
140
141 if decrypted.len() < 4 {
143 return Err(Error::DecryptionFailed);
144 }
145
146 let original_len_bytes: [u8; 4] = decrypted
147 .get(..4)
148 .ok_or(Error::DecryptionFailed)?
149 .try_into()
150 .map_err(|_| Error::DecryptionFailed)?;
151 let original_len = usize::try_from(u32::from_le_bytes(original_len_bytes))
152 .map_err(|_| Error::invalid_format("decrypted payload length is too large"))?;
153
154 let full_len = encrypted_data.len();
155
156 if original_len > decrypted.len()
158 || original_len <= full_len.saturating_sub(16)
159 || original_len < 4
160 {
161 return Err(Error::invalid_format(format!(
162 "invalid decrypted length: {}, full_len: {}, decrypted size: {}",
163 original_len,
164 full_len,
165 decrypted.len()
166 )));
167 }
168
169 decrypted
171 .get(4..original_len)
172 .map(ToOwned::to_owned)
173 .ok_or_else(|| Error::invalid_format("invalid decrypted payload bounds"))
174}
175
176fn prepare_aes_oldmtp(
181 auth_key: &[u8; AUTH_KEY_SIZE],
182 msg_key: &[u8; AES_BLOCK_SIZE],
183) -> Result<([u8; AES_KEY_SIZE], [u8; AES_KEY_SIZE])> {
184 let auth_range = |range| {
185 auth_key
186 .get(range)
187 .ok_or_else(|| Error::invalid_format("auth key is too short for AES derivation"))
188 };
189
190 let sha1_a = sha1_hash_2(msg_key, auth_range(8..40)?);
192
193 let sha1_b = sha1_hash_3(auth_range(40..56)?, msg_key, auth_range(56..72)?);
195
196 let sha1_c = sha1_hash_2(auth_range(72..104)?, msg_key);
198
199 let sha1_d = sha1_hash_2(msg_key, auth_range(104..136)?);
201
202 let key_bytes: Vec<u8> = sha1_a
204 .iter()
205 .take(8)
206 .chain(sha1_b.iter().skip(8).take(12))
207 .chain(sha1_c.iter().skip(4).take(12))
208 .copied()
209 .collect();
210 let key = key_bytes
211 .try_into()
212 .map_err(|_| Error::invalid_format("failed to derive AES key"))?;
213
214 let iv_bytes: Vec<u8> = sha1_a
216 .iter()
217 .skip(8)
218 .take(12)
219 .chain(sha1_b.iter().take(8))
220 .chain(sha1_c.iter().skip(16).take(4))
221 .chain(sha1_d.iter().take(8))
222 .copied()
223 .collect();
224 let iv = iv_bytes
225 .try_into()
226 .map_err(|_| Error::invalid_format("failed to derive AES IV"))?;
227
228 Ok((key, iv))
229}
230
231fn ige_decrypt(key: &[u8; 32], iv: &[u8; 32], data: &[u8]) -> Vec<u8> {
233 use grammers_crypto::aes::ige_decrypt as grammers_ige_decrypt;
234
235 let mut decrypted = data.to_vec();
236 grammers_ige_decrypt(&mut decrypted, key, iv);
237 decrypted
238}
239
240fn sha1_hash(data: &[u8]) -> [u8; 20] {
242 let mut hasher = Sha1::new();
243 hasher.update(data);
244 hasher.finalize().into()
245}
246
247fn sha1_hash_2(a: &[u8], b: &[u8]) -> [u8; 20] {
249 let mut hasher = Sha1::new();
250 hasher.update(a);
251 hasher.update(b);
252 hasher.finalize().into()
253}
254
255fn sha1_hash_3(a: &[u8], b: &[u8], c: &[u8]) -> [u8; 20] {
257 let mut hasher = Sha1::new();
258 hasher.update(a);
259 hasher.update(b);
260 hasher.update(c);
261 hasher.finalize().into()
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 fn fixture_salt() -> [u8; LOCAL_ENCRYPT_SALT_SIZE] {
269 std::array::from_fn(|index| u8::try_from(index).unwrap_or_default())
272 }
273
274 #[test]
275 fn test_create_local_key_no_passcode() {
276 let salt = fixture_salt();
277 let passcode = b"";
278
279 let key = create_local_key(&salt, passcode);
280 assert_eq!(key.as_bytes().len(), AUTH_KEY_SIZE);
281 }
282
283 #[test]
284 fn test_create_local_key_with_passcode() {
285 let salt = fixture_salt();
286 let passcode = b"test";
287
288 let key = create_local_key(&salt, passcode);
289 assert_eq!(key.as_bytes().len(), AUTH_KEY_SIZE);
290
291 let key2 = create_local_key(&salt, passcode);
293 assert_eq!(key.as_bytes(), key2.as_bytes());
294 }
295
296 #[test]
297 fn test_auth_key_from_bytes() -> Result<()> {
298 let bytes = [0xAB; AUTH_KEY_SIZE];
299 let key = AuthKey::from_bytes(&bytes)?;
300 assert_eq!(key.as_bytes(), &bytes);
301 Ok(())
302 }
303
304 #[test]
305 fn test_auth_key_wrong_size() {
306 let bytes = [0u8; 100];
307 assert!(AuthKey::from_bytes(&bytes).is_err());
308 }
309
310 #[test]
311 fn test_sha1_hash() {
312 let data = b"hello";
313 let hash = sha1_hash(data);
314 assert_eq!(
316 hex::encode(hash),
317 "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
318 );
319 }
320}