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