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