Skip to main content

layer_crypto/
rsa.rs

1//! RSA padding used by Telegram's auth key exchange.
2
3use num_bigint::BigUint;
4use crate::{aes, sha256};
5
6/// An RSA public key (n, e).
7pub struct Key {
8    n: BigUint,
9    e: BigUint,
10}
11
12impl Key {
13    /// Parse decimal `n` and `e` strings.
14    pub fn new(n: &str, e: &str) -> Option<Self> {
15        Some(Self {
16            n: BigUint::parse_bytes(n.as_bytes(), 10)?,
17            e: BigUint::parse_bytes(e.as_bytes(), 10)?,
18        })
19    }
20}
21
22fn increment(data: &mut [u8]) {
23    let mut i = data.len() - 1;
24    loop {
25        let (n, overflow) = data[i].overflowing_add(1);
26        data[i] = n;
27        if overflow {
28            i = i.checked_sub(1).unwrap_or(data.len() - 1);
29        } else {
30            break;
31        }
32    }
33}
34
35/// RSA-encrypt `data` using the MTProto RSA-PAD scheme.
36///
37/// `random_bytes` must be exactly 224 bytes of secure random data.
38/// `data` must be ≤ 144 bytes.
39pub fn encrypt_hashed(data: &[u8], key: &Key, random_bytes: &[u8; 224]) -> Vec<u8> {
40    assert!(data.len() <= 144, "data too large for RSA-PAD");
41
42    // data_with_padding: 192 bytes
43    let mut data_with_padding = Vec::with_capacity(192);
44    data_with_padding.extend_from_slice(data);
45    data_with_padding.extend_from_slice(&random_bytes[..192 - data.len()]);
46
47    // data_pad_reversed
48    let data_pad_reversed: Vec<u8> = data_with_padding.iter().copied().rev().collect();
49
50    let mut temp_key: [u8; 32] = random_bytes[192..].try_into().unwrap();
51
52    let key_aes_encrypted = loop {
53        // data_with_hash = data_pad_reversed + SHA256(temp_key + data_with_padding)
54        let mut data_with_hash = Vec::with_capacity(224);
55        data_with_hash.extend_from_slice(&data_pad_reversed);
56        data_with_hash.extend_from_slice(&sha256!(&temp_key, &data_with_padding));
57
58        aes::ige_encrypt(&mut data_with_hash, &temp_key, &[0u8; 32]);
59
60        // temp_key_xor = temp_key XOR SHA256(aes_encrypted)
61        let hash = sha256!(&data_with_hash);
62        let mut xored = temp_key;
63        for (a, b) in xored.iter_mut().zip(hash.iter()) { *a ^= b; }
64
65        let mut candidate = Vec::with_capacity(256);
66        candidate.extend_from_slice(&xored);
67        candidate.extend_from_slice(&data_with_hash);
68
69        if BigUint::from_bytes_be(&candidate) < key.n {
70            break candidate;
71        }
72        increment(&mut temp_key);
73    };
74
75    let payload = BigUint::from_bytes_be(&key_aes_encrypted);
76    let encrypted = payload.modpow(&key.e, &key.n);
77    let mut block = encrypted.to_bytes_be();
78    while block.len() < 256 { block.insert(0, 0); }
79    block
80}