Skip to main content

layer_crypto/
lib.rs

1//! Cryptographic primitives for Telegram MTProto.
2//!
3//! Provides:
4//! - AES-256-IGE encryption/decryption
5//! - SHA-1 / SHA-256 hash macros
6//! - Pollard-rho PQ factorization
7//! - RSA padding (MTProto RSA-PAD scheme)
8//! - `AuthKey` — 256-byte session key
9//! - MTProto 2.0 message encryption / decryption
10//! - DH nonce→key derivation
11
12#![deny(unsafe_code)]
13
14pub mod aes;
15mod auth_key;
16mod deque_buffer;
17mod factorize;
18pub mod rsa;
19mod sha;
20
21pub use auth_key::AuthKey;
22pub use deque_buffer::DequeBuffer;
23pub use factorize::factorize;
24
25// ─── MTProto 2.0 encrypt / decrypt ───────────────────────────────────────────
26
27/// Errors from [`decrypt_data_v2`].
28#[derive(Clone, Debug, PartialEq)]
29pub enum DecryptError {
30    /// Ciphertext too short or not block-aligned.
31    InvalidBuffer,
32    /// The `auth_key_id` in the ciphertext does not match our key.
33    AuthKeyMismatch,
34    /// The `msg_key` in the ciphertext does not match our computed value.
35    MessageKeyMismatch,
36}
37
38impl std::fmt::Display for DecryptError {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            Self::InvalidBuffer => write!(f, "invalid ciphertext buffer length"),
42            Self::AuthKeyMismatch => write!(f, "auth_key_id mismatch"),
43            Self::MessageKeyMismatch => write!(f, "msg_key mismatch"),
44        }
45    }
46}
47impl std::error::Error for DecryptError {}
48
49enum Side { Client, Server }
50impl Side {
51    fn x(&self) -> usize { match self { Side::Client => 0, Side::Server => 8 } }
52}
53
54fn calc_key(auth_key: &AuthKey, msg_key: &[u8; 16], side: Side) -> ([u8; 32], [u8; 32]) {
55    let x = side.x();
56    let sha_a = sha256!(msg_key, &auth_key.data[x..x + 36]);
57    let sha_b = sha256!(&auth_key.data[40 + x..40 + x + 36], msg_key);
58
59    let mut aes_key = [0u8; 32];
60    aes_key[..8].copy_from_slice(&sha_a[..8]);
61    aes_key[8..24].copy_from_slice(&sha_b[8..24]);
62    aes_key[24..].copy_from_slice(&sha_a[24..]);
63
64    let mut aes_iv = [0u8; 32];
65    aes_iv[..8].copy_from_slice(&sha_b[..8]);
66    aes_iv[8..24].copy_from_slice(&sha_a[8..24]);
67    aes_iv[24..].copy_from_slice(&sha_b[24..]);
68
69    (aes_key, aes_iv)
70}
71
72fn padding_len(len: usize) -> usize {
73    16 + (16 - (len % 16))
74}
75
76/// Encrypt `buffer` (in-place, with prepended header) using MTProto 2.0.
77///
78/// After this call `buffer` contains `key_id || msg_key || ciphertext`.
79pub fn encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey) {
80    let mut rnd = [0u8; 32];
81    getrandom::getrandom(&mut rnd).expect("getrandom failed");
82    do_encrypt_data_v2(buffer, auth_key, &rnd);
83}
84
85pub(crate) fn do_encrypt_data_v2(buffer: &mut DequeBuffer, auth_key: &AuthKey, rnd: &[u8; 32]) {
86    let pad = padding_len(buffer.len());
87    buffer.extend(rnd.iter().take(pad).copied());
88
89    let x = Side::Client.x();
90    let msg_key_large = sha256!(&auth_key.data[88 + x..88 + x + 32], buffer.as_ref());
91    let mut msg_key = [0u8; 16];
92    msg_key.copy_from_slice(&msg_key_large[8..24]);
93
94    let (key, iv) = calc_key(auth_key, &msg_key, Side::Client);
95    aes::ige_encrypt(buffer.as_mut(), &key, &iv);
96
97    buffer.extend_front(&msg_key);
98    buffer.extend_front(&auth_key.key_id);
99}
100
101/// Decrypt an MTProto 2.0 ciphertext.
102///
103/// `buffer` must start with `key_id || msg_key || ciphertext`.
104/// On success returns a slice of `buffer` containing the plaintext.
105pub fn decrypt_data_v2<'a>(buffer: &'a mut [u8], auth_key: &AuthKey) -> Result<&'a mut [u8], DecryptError> {
106    if buffer.len() < 24 || (buffer.len() - 24) % 16 != 0 {
107        return Err(DecryptError::InvalidBuffer);
108    }
109    if auth_key.key_id != buffer[..8] {
110        return Err(DecryptError::AuthKeyMismatch);
111    }
112    let mut msg_key = [0u8; 16];
113    msg_key.copy_from_slice(&buffer[8..24]);
114
115    let (key, iv) = calc_key(auth_key, &msg_key, Side::Server);
116    aes::ige_decrypt(&mut buffer[24..], &key, &iv);
117
118    let x = Side::Server.x();
119    let our_key = sha256!(&auth_key.data[88 + x..88 + x + 32], &buffer[24..]);
120    if msg_key != our_key[8..24] {
121        return Err(DecryptError::MessageKeyMismatch);
122    }
123    Ok(&mut buffer[24..])
124}
125
126/// Derive `(key, iv)` from nonces for decrypting `ServerDhParams.encrypted_answer`.
127pub fn generate_key_data_from_nonce(server_nonce: &[u8; 16], new_nonce: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
128    let h1 = sha1!(new_nonce, server_nonce);
129    let h2 = sha1!(server_nonce, new_nonce);
130    let h3 = sha1!(new_nonce, new_nonce);
131
132    let mut key = [0u8; 32];
133    key[..20].copy_from_slice(&h1);
134    key[20..].copy_from_slice(&h2[..12]);
135
136    let mut iv = [0u8; 32];
137    iv[..8].copy_from_slice(&h2[12..]);
138    iv[8..28].copy_from_slice(&h3);
139    iv[28..].copy_from_slice(&new_nonce[..4]);
140
141    (key, iv)
142}