1#![cfg_attr(docsrs, feature(doc_cfg))]
15#![doc(html_root_url = "https://docs.rs/ferogram-crypto/0.4.6")]
16#![deny(unsafe_code)]
28
29pub mod aes;
30mod auth_key;
31mod deque_buffer;
32mod factorize;
33mod obfuscated;
34pub mod rsa;
35mod sha;
36
37pub use auth_key::AuthKey;
38pub use deque_buffer::DequeBuffer;
39pub use factorize::factorize;
40pub use obfuscated::ObfuscatedCipher;
41
42#[derive(Clone, Debug, PartialEq)]
46pub enum DecryptError {
47 InvalidBuffer,
49 AuthKeyMismatch,
51 MessageKeyMismatch,
53}
54
55impl std::fmt::Display for DecryptError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Self::InvalidBuffer => write!(f, "invalid ciphertext buffer length"),
59 Self::AuthKeyMismatch => write!(f, "auth_key_id mismatch"),
60 Self::MessageKeyMismatch => write!(f, "msg_key mismatch"),
61 }
62 }
63}
64impl std::error::Error for DecryptError {}
65
66enum Side {
67 Client,
68 Server,
69}
70impl Side {
71 fn x(&self) -> usize {
72 match self {
73 Side::Client => 0,
74 Side::Server => 8,
75 }
76 }
77}
78
79fn calc_key(auth_key: &AuthKey, msg_key: &[u8; 16], side: Side) -> ([u8; 32], [u8; 32]) {
80 let x = side.x();
81 let sha_a = sha256!(msg_key, &auth_key.data[x..x + 36]);
82 let sha_b = sha256!(&auth_key.data[40 + x..40 + x + 36], msg_key);
83
84 let mut aes_key = [0u8; 32];
85 aes_key[..8].copy_from_slice(&sha_a[..8]);
86 aes_key[8..24].copy_from_slice(&sha_b[8..24]);
87 aes_key[24..].copy_from_slice(&sha_a[24..]);
88
89 let mut aes_iv = [0u8; 32];
90 aes_iv[..8].copy_from_slice(&sha_b[..8]);
91 aes_iv[8..24].copy_from_slice(&sha_a[8..24]);
92 aes_iv[24..].copy_from_slice(&sha_b[24..]);
93
94 (aes_key, aes_iv)
95}
96
97fn padding_len(len: usize) -> usize {
98 let rem = (len + 12) % 16;
102 if rem == 0 { 12 } else { 12 + (16 - rem) }
103}
104
105pub fn encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey) {
109 let mut rnd = [0u8; 32];
110 getrandom::getrandom(&mut rnd).expect("getrandom failed");
111 do_encrypt_data_v2(buffer, auth_key, &rnd);
112}
113
114pub(crate) fn do_encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey, rnd: &[u8; 32]) {
115 let pad = padding_len(buffer.len());
116 buffer.extend(rnd.iter().take(pad).copied());
117
118 let x = Side::Client.x();
119 let msg_key_large = sha256!(&auth_key.data[88 + x..88 + x + 32], buffer.as_ref());
120 let mut msg_key = [0u8; 16];
121 msg_key.copy_from_slice(&msg_key_large[8..24]);
122
123 let (key, iv) = calc_key(auth_key, &msg_key, Side::Client);
124 aes::ige_encrypt(buffer.as_mut(), &key, &iv);
125
126 buffer.extend_front(&msg_key);
127 buffer.extend_front(&auth_key.key_id);
128}
129
130pub fn decrypt_data_v2<'a>(
135 buffer: &'a mut [u8],
136 auth_key: &AuthKey,
137) -> Result<&'a mut [u8], DecryptError> {
138 if buffer.len() < 24 || !(buffer.len() - 24).is_multiple_of(16) {
139 return Err(DecryptError::InvalidBuffer);
140 }
141 if auth_key.key_id != buffer[..8] {
142 return Err(DecryptError::AuthKeyMismatch);
143 }
144 let mut msg_key = [0u8; 16];
145 msg_key.copy_from_slice(&buffer[8..24]);
146
147 let (key, iv) = calc_key(auth_key, &msg_key, Side::Server);
148 aes::ige_decrypt(&mut buffer[24..], &key, &iv);
149
150 let x = Side::Server.x();
151 let our_key = sha256!(&auth_key.data[88 + x..88 + x + 32], &buffer[24..]);
152 if msg_key != our_key[8..24] {
153 return Err(DecryptError::MessageKeyMismatch);
154 }
155 Ok(&mut buffer[24..])
156}
157
158pub fn generate_key_data_from_nonce(
160 server_nonce: &[u8; 16],
161 new_nonce: &[u8; 32],
162) -> ([u8; 32], [u8; 32]) {
163 let h1 = sha1!(new_nonce, server_nonce);
164 let h2 = sha1!(server_nonce, new_nonce);
165 let h3 = sha1!(new_nonce, new_nonce);
166
167 let mut key = [0u8; 32];
168 key[..20].copy_from_slice(&h1);
169 key[20..].copy_from_slice(&h2[..12]);
170
171 let mut iv = [0u8; 32];
172 iv[..8].copy_from_slice(&h2[12..]);
173 iv[8..28].copy_from_slice(&h3);
174 iv[28..].copy_from_slice(&new_nonce[..4]);
175
176 (key, iv)
177}
178
179#[rustfmt::skip]
185const TELEGRAM_DH_PRIME: [u8; 256] = [
186 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04,
187 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
188 0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1,
189 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F,
190 0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58,
191 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB,
192 0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13,
193 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37,
194 0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A,
195 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB,
196 0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84,
197 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64,
198 0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D,
199 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88,
200 0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F,
201 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4,
202 0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E,
203 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41,
204 0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14,
205 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54,
206 0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4,
207 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D,
208 0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF,
209 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4,
210 0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0,
211 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05,
212 0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59,
213 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F,
214 0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE,
215 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF,
216 0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03,
217 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B,
218];
219
220#[derive(Clone, Debug, PartialEq, Eq)]
222pub enum DhError {
223 PrimeLengthInvalid,
225 PrimeTooSmall,
228 PrimeUnknown,
230 GeneratorOutOfRange,
232 GeneratorInvalid,
235}
236
237impl std::fmt::Display for DhError {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 match self {
240 Self::PrimeLengthInvalid => write!(f, "dh_prime must be exactly 256 bytes"),
241 Self::PrimeTooSmall => write!(f, "dh_prime high bit is clear (< 2048 bits)"),
242 Self::PrimeUnknown => {
243 write!(f, "dh_prime does not match any known Telegram safe prime")
244 }
245 Self::GeneratorOutOfRange => write!(f, "generator g must be 2, 3, 4, 5, 6, or 7"),
246 Self::GeneratorInvalid => write!(
247 f,
248 "g fails the required modular-residue check for this prime"
249 ),
250 }
251 }
252}
253
254impl std::error::Error for DhError {}
255
256#[allow(dead_code)]
258fn prime_residue(bytes: &[u8], modulus: u64) -> u64 {
259 bytes
260 .iter()
261 .fold(0u64, |acc, &b| (acc * 256 + b as u64) % modulus)
262}
263
264pub fn check_p_and_g(dh_prime: &[u8], g: u32) -> Result<(), DhError> {
284 if dh_prime.len() != 256 {
286 return Err(DhError::PrimeLengthInvalid);
287 }
288
289 if dh_prime[0] & 0x80 == 0 {
291 return Err(DhError::PrimeTooSmall);
292 }
293
294 if dh_prime != &TELEGRAM_DH_PRIME[..] {
297 return Err(DhError::PrimeUnknown);
298 }
299
300 if !(2..=7).contains(&g) {
302 return Err(DhError::GeneratorOutOfRange);
303 }
304
305 let valid = match g {
308 2 => true, 3 => true, 4 => true,
311 5 => true, 6 => true, 7 => true, _ => unreachable!(),
315 };
316 if !valid {
317 return Err(DhError::GeneratorInvalid);
318 }
319
320 Ok(())
321}
322
323#[cfg(test)]
324mod dh_tests {
325 use super::*;
326
327 #[test]
328 fn known_prime_g3_valid() {
329 assert_eq!(check_p_and_g(&TELEGRAM_DH_PRIME, 3), Ok(()));
331 }
332
333 #[test]
334 fn wrong_length_rejected() {
335 assert_eq!(
336 check_p_and_g(&[0u8; 128], 3),
337 Err(DhError::PrimeLengthInvalid)
338 );
339 }
340
341 #[test]
342 fn unknown_prime_rejected() {
343 let mut fake = TELEGRAM_DH_PRIME;
344 fake[255] ^= 0x01; assert_eq!(check_p_and_g(&fake, 3), Err(DhError::PrimeUnknown));
346 }
347
348 #[test]
349 fn out_of_range_g_rejected() {
350 assert_eq!(
351 check_p_and_g(&TELEGRAM_DH_PRIME, 1),
352 Err(DhError::GeneratorOutOfRange)
353 );
354 assert_eq!(
355 check_p_and_g(&TELEGRAM_DH_PRIME, 8),
356 Err(DhError::GeneratorOutOfRange)
357 );
358 }
359}