1#![cfg_attr(docsrs, feature(doc_cfg))]
14#![doc(html_root_url = "https://docs.rs/ferogram-crypto/0.3.7")]
15#![deny(unsafe_code)]
75
76pub mod aes;
77mod auth_key;
78mod deque_buffer;
79mod factorize;
80mod obfuscated;
81pub mod rsa;
82mod sha;
83
84pub use auth_key::AuthKey;
85pub use deque_buffer::DequeBuffer;
86pub use factorize::factorize;
87pub use obfuscated::ObfuscatedCipher;
88
89#[derive(Clone, Debug, PartialEq)]
91pub enum DecryptError {
92 InvalidBuffer,
94 AuthKeyMismatch,
96 MessageKeyMismatch,
98}
99
100impl std::fmt::Display for DecryptError {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 Self::InvalidBuffer => write!(f, "invalid ciphertext buffer length"),
104 Self::AuthKeyMismatch => write!(f, "auth_key_id mismatch"),
105 Self::MessageKeyMismatch => write!(f, "msg_key mismatch"),
106 }
107 }
108}
109impl std::error::Error for DecryptError {}
110
111enum Side {
112 Client,
113 Server,
114}
115impl Side {
116 fn x(&self) -> usize {
117 match self {
118 Side::Client => 0,
119 Side::Server => 8,
120 }
121 }
122}
123
124fn calc_key(auth_key: &AuthKey, msg_key: &[u8; 16], side: Side) -> ([u8; 32], [u8; 32]) {
125 let x = side.x();
126 let sha_a = sha256!(msg_key, &auth_key.data[x..x + 36]);
127 let sha_b = sha256!(&auth_key.data[40 + x..40 + x + 36], msg_key);
128
129 let mut aes_key = [0u8; 32];
130 aes_key[..8].copy_from_slice(&sha_a[..8]);
131 aes_key[8..24].copy_from_slice(&sha_b[8..24]);
132 aes_key[24..].copy_from_slice(&sha_a[24..]);
133
134 let mut aes_iv = [0u8; 32];
135 aes_iv[..8].copy_from_slice(&sha_b[..8]);
136 aes_iv[8..24].copy_from_slice(&sha_a[8..24]);
137 aes_iv[24..].copy_from_slice(&sha_b[24..]);
138
139 (aes_key, aes_iv)
140}
141
142fn padding_len(len: usize) -> usize {
143 let rem = (len + 12) % 16;
147 if rem == 0 { 12 } else { 12 + (16 - rem) }
148}
149
150pub fn encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey) {
154 let mut rnd = [0u8; 32];
155 getrandom::getrandom(&mut rnd).expect("getrandom failed");
156 do_encrypt_data_v2(buffer, auth_key, &rnd);
157}
158
159pub(crate) fn do_encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey, rnd: &[u8; 32]) {
160 let pad = padding_len(buffer.len());
161 buffer.extend(rnd.iter().take(pad).copied());
162
163 let x = Side::Client.x();
164 let msg_key_large = sha256!(&auth_key.data[88 + x..88 + x + 32], buffer.as_ref());
165 let mut msg_key = [0u8; 16];
166 msg_key.copy_from_slice(&msg_key_large[8..24]);
167
168 let (key, iv) = calc_key(auth_key, &msg_key, Side::Client);
169 aes::ige_encrypt(buffer.as_mut(), &key, &iv);
170
171 buffer.extend_front(&msg_key);
172 buffer.extend_front(&auth_key.key_id);
173}
174
175pub fn decrypt_data_v2<'a>(
180 buffer: &'a mut [u8],
181 auth_key: &AuthKey,
182) -> Result<&'a mut [u8], DecryptError> {
183 if buffer.len() < 24 || !(buffer.len() - 24).is_multiple_of(16) {
184 return Err(DecryptError::InvalidBuffer);
185 }
186 if auth_key.key_id != buffer[..8] {
187 return Err(DecryptError::AuthKeyMismatch);
188 }
189 let mut msg_key = [0u8; 16];
190 msg_key.copy_from_slice(&buffer[8..24]);
191
192 let (key, iv) = calc_key(auth_key, &msg_key, Side::Server);
193 aes::ige_decrypt(&mut buffer[24..], &key, &iv);
194
195 let x = Side::Server.x();
196 let our_key = sha256!(&auth_key.data[88 + x..88 + x + 32], &buffer[24..]);
197 if msg_key != our_key[8..24] {
198 return Err(DecryptError::MessageKeyMismatch);
199 }
200 Ok(&mut buffer[24..])
201}
202
203pub fn generate_key_data_from_nonce(
205 server_nonce: &[u8; 16],
206 new_nonce: &[u8; 32],
207) -> ([u8; 32], [u8; 32]) {
208 let h1 = sha1!(new_nonce, server_nonce);
209 let h2 = sha1!(server_nonce, new_nonce);
210 let h3 = sha1!(new_nonce, new_nonce);
211
212 let mut key = [0u8; 32];
213 key[..20].copy_from_slice(&h1);
214 key[20..].copy_from_slice(&h2[..12]);
215
216 let mut iv = [0u8; 32];
217 iv[..8].copy_from_slice(&h2[12..]);
218 iv[8..28].copy_from_slice(&h3);
219 iv[28..].copy_from_slice(&new_nonce[..4]);
220
221 (key, iv)
222}
223
224pub fn derive_aes_key_iv_v1(auth_key: &[u8; 256], msg_key: &[u8; 16]) -> ([u8; 32], [u8; 32]) {
230 let sha1_a = sha1!(msg_key, &auth_key[0..32]);
231 let sha1_b = sha1!(&auth_key[32..48], msg_key, &auth_key[48..64]);
232 let sha1_c = sha1!(&auth_key[64..96], msg_key);
233 let sha1_d = sha1!(msg_key, &auth_key[96..128]);
234
235 let mut key = [0u8; 32];
236 key[..8].copy_from_slice(&sha1_a[..8]);
237 key[8..20].copy_from_slice(&sha1_b[8..20]);
238 key[20..32].copy_from_slice(&sha1_c[4..16]);
239
240 let mut iv = [0u8; 32];
241 iv[..12].copy_from_slice(&sha1_a[8..20]);
242 iv[12..20].copy_from_slice(&sha1_b[..8]);
243 iv[20..24].copy_from_slice(&sha1_c[16..20]);
244 iv[24..32].copy_from_slice(&sha1_d[..8]);
245
246 (key, iv)
247}
248
249#[rustfmt::skip]
253const TELEGRAM_DH_PRIME: [u8; 256] = [
254 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04,
255 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
256 0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1,
257 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F,
258 0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58,
259 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB,
260 0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13,
261 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37,
262 0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A,
263 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB,
264 0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84,
265 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64,
266 0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D,
267 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88,
268 0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F,
269 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4,
270 0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E,
271 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41,
272 0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14,
273 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54,
274 0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4,
275 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D,
276 0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF,
277 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4,
278 0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0,
279 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05,
280 0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59,
281 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F,
282 0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE,
283 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF,
284 0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03,
285 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B,
286];
287
288#[derive(Clone, Debug, PartialEq, Eq)]
290pub enum DhError {
291 PrimeLengthInvalid,
293 PrimeTooSmall,
296 PrimeUnknown,
298 GeneratorOutOfRange,
300 GeneratorInvalid,
303}
304
305impl std::fmt::Display for DhError {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 match self {
308 Self::PrimeLengthInvalid => write!(f, "dh_prime must be exactly 256 bytes"),
309 Self::PrimeTooSmall => write!(f, "dh_prime high bit is clear (< 2048 bits)"),
310 Self::PrimeUnknown => {
311 write!(f, "dh_prime does not match any known Telegram safe prime")
312 }
313 Self::GeneratorOutOfRange => write!(f, "generator g must be 2, 3, 4, 5, 6, or 7"),
314 Self::GeneratorInvalid => write!(
315 f,
316 "g fails the required modular-residue check for this prime"
317 ),
318 }
319 }
320}
321
322impl std::error::Error for DhError {}
323
324fn prime_residue(bytes: &[u8], modulus: u64) -> u64 {
326 bytes
327 .iter()
328 .fold(0u64, |acc, &b| (acc * 256 + b as u64) % modulus)
329}
330
331pub fn check_p_and_g(dh_prime: &[u8], g: u32) -> Result<(), DhError> {
351 if dh_prime.len() != 256 {
353 return Err(DhError::PrimeLengthInvalid);
354 }
355
356 if dh_prime[0] & 0x80 == 0 {
358 return Err(DhError::PrimeTooSmall);
359 }
360
361 if dh_prime != &TELEGRAM_DH_PRIME[..] {
364 return Err(DhError::PrimeUnknown);
365 }
366
367 if !(2..=7).contains(&g) {
369 return Err(DhError::GeneratorOutOfRange);
370 }
371
372 let valid = match g {
374 2 => prime_residue(dh_prime, 8) == 7,
375 3 => prime_residue(dh_prime, 3) == 2,
376 4 => true,
377 5 => {
378 let r = prime_residue(dh_prime, 5);
379 r == 1 || r == 4
380 }
381 6 => {
382 let r = prime_residue(dh_prime, 24);
383 r == 19 || r == 23
384 }
385 7 => {
386 let r = prime_residue(dh_prime, 7);
387 r == 3 || r == 5 || r == 6
388 }
389 _ => unreachable!(),
390 };
391 if !valid {
392 return Err(DhError::GeneratorInvalid);
393 }
394
395 Ok(())
396}
397
398#[cfg(test)]
399mod dh_tests {
400 use super::*;
401
402 #[test]
403 fn known_prime_g3_valid() {
404 assert_eq!(check_p_and_g(&TELEGRAM_DH_PRIME, 3), Ok(()));
406 }
407
408 #[test]
409 fn wrong_length_rejected() {
410 assert_eq!(
411 check_p_and_g(&[0u8; 128], 3),
412 Err(DhError::PrimeLengthInvalid)
413 );
414 }
415
416 #[test]
417 fn unknown_prime_rejected() {
418 let mut fake = TELEGRAM_DH_PRIME;
419 fake[255] ^= 0x01; assert_eq!(check_p_and_g(&fake, 3), Err(DhError::PrimeUnknown));
421 }
422
423 #[test]
424 fn out_of_range_g_rejected() {
425 assert_eq!(
426 check_p_and_g(&TELEGRAM_DH_PRIME, 1),
427 Err(DhError::GeneratorOutOfRange)
428 );
429 assert_eq!(
430 check_p_and_g(&TELEGRAM_DH_PRIME, 8),
431 Err(DhError::GeneratorOutOfRange)
432 );
433 }
434}