1#![cfg_attr(docsrs, feature(doc_cfg))]
16#![doc(html_root_url = "https://docs.rs/ferogram-crypto/0.6.5")]
17#![deny(unsafe_code)]
77
78pub mod aes;
79mod auth_key;
80mod deque_buffer;
81pub mod dh;
82mod factorize;
83mod obfuscated;
84pub mod rsa;
85mod sha;
86pub mod srp;
87
88pub use auth_key::AuthKey;
89pub use deque_buffer::DequeBuffer;
90pub use factorize::factorize;
91pub use obfuscated::{
92 ObfuscatedCipher, build_obfuscated_init, fake_tls_client_digest, fake_tls_verify_server_digest,
93};
94
95pub fn fill_random(buf: &mut [u8]) {
99 getrandom::fill(buf).expect("OS RNG unavailable");
100}
101
102#[derive(Clone, Debug, PartialEq)]
104pub enum DecryptError {
105 InvalidBuffer,
107 AuthKeyMismatch,
109 MessageKeyMismatch,
111}
112
113impl std::fmt::Display for DecryptError {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 match self {
116 Self::InvalidBuffer => write!(f, "invalid ciphertext buffer length"),
117 Self::AuthKeyMismatch => write!(f, "auth_key_id mismatch"),
118 Self::MessageKeyMismatch => write!(f, "msg_key mismatch"),
119 }
120 }
121}
122impl std::error::Error for DecryptError {}
123
124enum Side {
125 Client,
126 Server,
127}
128impl Side {
129 fn x(&self) -> usize {
130 match self {
131 Side::Client => 0,
132 Side::Server => 8,
133 }
134 }
135}
136
137fn calc_key(auth_key: &AuthKey, msg_key: &[u8; 16], side: Side) -> ([u8; 32], [u8; 32]) {
138 let x = side.x();
139 let sha_a = sha256!(msg_key, &auth_key.data[x..x + 36]);
140 let sha_b = sha256!(&auth_key.data[40 + x..40 + x + 36], msg_key);
141
142 let mut aes_key = [0u8; 32];
143 aes_key[..8].copy_from_slice(&sha_a[..8]);
144 aes_key[8..24].copy_from_slice(&sha_b[8..24]);
145 aes_key[24..].copy_from_slice(&sha_a[24..]);
146
147 let mut aes_iv = [0u8; 32];
148 aes_iv[..8].copy_from_slice(&sha_b[..8]);
149 aes_iv[8..24].copy_from_slice(&sha_a[8..24]);
150 aes_iv[24..].copy_from_slice(&sha_b[24..]);
151
152 (aes_key, aes_iv)
153}
154
155fn padding_len(len: usize) -> usize {
156 let rem = (len + 12) % 16;
160 if rem == 0 { 12 } else { 12 + (16 - rem) }
161}
162
163pub fn encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey) {
167 let mut rnd = [0u8; 32];
168 getrandom::fill(&mut rnd).expect("getrandom failed");
169 do_encrypt_data_v2(buffer, auth_key, &rnd);
170}
171
172pub(crate) fn do_encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey, rnd: &[u8; 32]) {
173 let pad = padding_len(buffer.len());
174 buffer.extend(rnd.iter().take(pad).copied());
175
176 let x = Side::Client.x();
177 let msg_key_large = sha256!(&auth_key.data[88 + x..88 + x + 32], buffer.as_ref());
178 let mut msg_key = [0u8; 16];
179 msg_key.copy_from_slice(&msg_key_large[8..24]);
180
181 let (key, iv) = calc_key(auth_key, &msg_key, Side::Client);
182 aes::ige_encrypt(buffer.as_mut(), &key, &iv);
183
184 buffer.extend_front(&msg_key);
185 buffer.extend_front(&auth_key.key_id);
186}
187
188pub fn decrypt_data_v2<'a>(
193 buffer: &'a mut [u8],
194 auth_key: &AuthKey,
195) -> Result<&'a mut [u8], DecryptError> {
196 let frame_len = buffer.len();
198 let frame_hex: String = buffer
199 .iter()
200 .take(32)
201 .map(|b| format!("{b:02x}"))
202 .collect::<Vec<_>>()
203 .join(" ");
204
205 let usable_cipher_len = buffer.len().saturating_sub(24) / 16 * 16;
214 if buffer.len() < 24 || usable_cipher_len == 0 {
215 tracing::warn!(
219 frame_len,
220 first_bytes = %frame_hex,
221 "decrypt failed: frame too short to contain even one AES block \
222 (need >= 40 bytes)"
223 );
224 return Err(DecryptError::InvalidBuffer);
225 }
226
227 let our_key_id = auth_key.key_id();
228 let frame_key_id = &buffer[..8];
229 if our_key_id != frame_key_id {
230 let our_hex = our_key_id
235 .iter()
236 .map(|b| format!("{b:02x}"))
237 .collect::<Vec<_>>()
238 .join("");
239 let frame_hex_id = frame_key_id
240 .iter()
241 .map(|b| format!("{b:02x}"))
242 .collect::<Vec<_>>()
243 .join("");
244 tracing::warn!(
245 frame_len,
246 our_key_id = %our_hex,
247 frame_key_id = %frame_hex_id,
248 first_bytes = %frame_hex,
249 "decrypt failed: auth_key_id mismatch (stale session or wrong DC key)"
250 );
251 return Err(DecryptError::AuthKeyMismatch);
252 }
253 let mut msg_key = [0u8; 16];
254 msg_key.copy_from_slice(&buffer[8..24]);
255
256 let (key, iv) = calc_key(auth_key, &msg_key, Side::Server);
257 let cipher_end = 24 + usable_cipher_len;
258 aes::ige_decrypt(&mut buffer[24..cipher_end], &key, &iv);
259
260 let x = Side::Server.x();
261 let our_key = sha256!(&auth_key.data[88 + x..88 + x + 32], &buffer[24..cipher_end]);
262 if msg_key != our_key[8..24] {
263 return Err(DecryptError::MessageKeyMismatch);
264 }
265 Ok(&mut buffer[24..cipher_end])
266}
267
268pub fn generate_key_data_from_nonce(
270 server_nonce: &[u8; 16],
271 new_nonce: &[u8; 32],
272) -> ([u8; 32], [u8; 32]) {
273 let h1 = sha1!(new_nonce, server_nonce);
274 let h2 = sha1!(server_nonce, new_nonce);
275 let h3 = sha1!(new_nonce, new_nonce);
276
277 let mut key = [0u8; 32];
278 key[..20].copy_from_slice(&h1);
279 key[20..].copy_from_slice(&h2[..12]);
280
281 let mut iv = [0u8; 32];
282 iv[..8].copy_from_slice(&h2[12..]);
283 iv[8..28].copy_from_slice(&h3);
284 iv[28..].copy_from_slice(&new_nonce[..4]);
285
286 (key, iv)
287}
288
289pub fn derive_aes_key_iv_v1(auth_key: &[u8; 256], msg_key: &[u8; 16]) -> ([u8; 32], [u8; 32]) {
295 let sha1_a = sha1!(msg_key, &auth_key[0..32]);
296 let sha1_b = sha1!(&auth_key[32..48], msg_key, &auth_key[48..64]);
297 let sha1_c = sha1!(&auth_key[64..96], msg_key);
298 let sha1_d = sha1!(msg_key, &auth_key[96..128]);
299
300 let mut key = [0u8; 32];
301 key[..8].copy_from_slice(&sha1_a[..8]);
302 key[8..20].copy_from_slice(&sha1_b[8..20]);
303 key[20..32].copy_from_slice(&sha1_c[4..16]);
304
305 let mut iv = [0u8; 32];
306 iv[..12].copy_from_slice(&sha1_a[8..20]);
307 iv[12..20].copy_from_slice(&sha1_b[..8]);
308 iv[20..24].copy_from_slice(&sha1_c[16..20]);
309 iv[24..32].copy_from_slice(&sha1_d[..8]);
310
311 (key, iv)
312}
313
314#[rustfmt::skip]
318const TELEGRAM_DH_PRIME: [u8; 256] = [
319 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04,
320 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
321 0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1,
322 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F,
323 0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58,
324 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB,
325 0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13,
326 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37,
327 0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A,
328 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB,
329 0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84,
330 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64,
331 0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D,
332 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88,
333 0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F,
334 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4,
335 0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E,
336 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41,
337 0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14,
338 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54,
339 0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4,
340 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D,
341 0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF,
342 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4,
343 0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0,
344 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05,
345 0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59,
346 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F,
347 0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE,
348 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF,
349 0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03,
350 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B,
351];
352
353#[derive(Clone, Debug, PartialEq, Eq)]
355pub enum DhError {
356 PrimeLengthInvalid,
358 PrimeTooSmall,
361 PrimeUnknown,
363 GeneratorOutOfRange,
365 GeneratorInvalid,
368}
369
370impl std::fmt::Display for DhError {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 match self {
373 Self::PrimeLengthInvalid => write!(f, "dh_prime must be exactly 256 bytes"),
374 Self::PrimeTooSmall => write!(f, "dh_prime high bit is clear (< 2048 bits)"),
375 Self::PrimeUnknown => {
376 write!(f, "dh_prime does not match any known Telegram safe prime")
377 }
378 Self::GeneratorOutOfRange => write!(f, "generator g must be 2, 3, 4, 5, 6, or 7"),
379 Self::GeneratorInvalid => write!(
380 f,
381 "g fails the required modular-residue check for this prime"
382 ),
383 }
384 }
385}
386
387impl std::error::Error for DhError {}
388
389fn prime_residue(bytes: &[u8], modulus: u64) -> u64 {
391 bytes
392 .iter()
393 .fold(0u64, |acc, &b| (acc * 256 + b as u64) % modulus)
394}
395
396pub fn check_p_and_g(dh_prime: &[u8], g: u32) -> Result<(), DhError> {
416 if dh_prime.len() != 256 {
418 return Err(DhError::PrimeLengthInvalid);
419 }
420
421 if dh_prime[0] & 0x80 == 0 {
423 return Err(DhError::PrimeTooSmall);
424 }
425
426 if dh_prime != &TELEGRAM_DH_PRIME[..] {
429 return Err(DhError::PrimeUnknown);
430 }
431
432 if !(2..=7).contains(&g) {
434 return Err(DhError::GeneratorOutOfRange);
435 }
436
437 let valid = match g {
439 2 => prime_residue(dh_prime, 8) == 7,
440 3 => prime_residue(dh_prime, 3) == 2,
441 4 => true,
442 5 => {
443 let r = prime_residue(dh_prime, 5);
444 r == 1 || r == 4
445 }
446 6 => {
447 let r = prime_residue(dh_prime, 24);
448 r == 19 || r == 23
449 }
450 7 => {
451 let r = prime_residue(dh_prime, 7);
452 r == 3 || r == 5 || r == 6
453 }
454 _ => unreachable!(),
455 };
456 if !valid {
457 return Err(DhError::GeneratorInvalid);
458 }
459
460 Ok(())
461}
462
463#[cfg(test)]
464mod dh_tests {
465 use super::*;
466
467 #[test]
468 fn known_prime_g3_valid() {
469 assert_eq!(check_p_and_g(&TELEGRAM_DH_PRIME, 3), Ok(()));
471 }
472
473 #[test]
474 fn wrong_length_rejected() {
475 assert_eq!(
476 check_p_and_g(&[0u8; 128], 3),
477 Err(DhError::PrimeLengthInvalid)
478 );
479 }
480
481 #[test]
482 fn unknown_prime_rejected() {
483 let mut fake = TELEGRAM_DH_PRIME;
484 fake[255] ^= 0x01; assert_eq!(check_p_and_g(&fake, 3), Err(DhError::PrimeUnknown));
486 }
487
488 #[test]
489 fn out_of_range_g_rejected() {
490 assert_eq!(
491 check_p_and_g(&TELEGRAM_DH_PRIME, 1),
492 Err(DhError::GeneratorOutOfRange)
493 );
494 assert_eq!(
495 check_p_and_g(&TELEGRAM_DH_PRIME, 8),
496 Err(DhError::GeneratorOutOfRange)
497 );
498 }
499}