Skip to main content

grammers_crypto/
rsa.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Contains the function to RSA-encrypt hashed data as expected by Telegram.
10//!
11//! Not intended as a general implementation of RSA.
12
13use num_bigint::BigUint;
14
15use crate::{aes, sha256};
16
17/// RSA key.
18pub struct Key {
19    n: BigUint,
20    e: BigUint,
21}
22
23impl Key {
24    pub fn new(n: &str, e: &str) -> Option<Self> {
25        Some(Self {
26            n: BigUint::parse_bytes(n.as_bytes(), 10)?,
27            e: BigUint::parse_bytes(e.as_bytes(), 10)?,
28        })
29    }
30}
31
32/// Increment data by 1 when interpreted as a big-endian big int.
33fn increment(data: &mut [u8]) {
34    let mut i = data.len() - 1;
35    loop {
36        let (n, overflow) = data[i].overflowing_add(1);
37        data[i] = n;
38        if overflow {
39            if let Some(ni) = i.checked_sub(1) {
40                i = ni
41            } else {
42                i = data.len() - 1;
43            }
44        } else {
45            break;
46        }
47    }
48}
49
50/// Encrypt the given data, prefixing it with a hash before, using RSA.
51pub fn encrypt_hashed(data: &[u8], key: &Key, random_bytes: &[u8; 224]) -> Vec<u8> {
52    // https://core.telegram.org/mtproto/auth_key#41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows
53
54    // data_with_padding := data + random_padding_bytes; -- where random_padding_bytes are chosen so that the resulting length of data_with_padding is precisely 192 bytes, and data is the TL-serialized data to be encrypted as before. One has to check that data is not longer than 144 bytes.
55    assert!(data.len() <= 144);
56    let data_with_padding = {
57        let mut buffer = Vec::with_capacity(192);
58        buffer.extend(data);
59        buffer.extend(&random_bytes[..192 - data.len()]);
60        buffer
61    };
62
63    // data_pad_reversed := BYTE_REVERSE(data_with_padding); -- is obtained from data_with_padding by reversing the byte order.
64    let data_pad_reversed = data_with_padding.iter().copied().rev().collect::<Vec<u8>>();
65
66    // a random 32-byte temp_key is generated.
67    let mut temp_key: [u8; 32] = random_bytes[192..192 + 32].try_into().unwrap();
68
69    let key_aes_encrypted = loop {
70        // data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding); -- after this assignment, data_with_hash is exactly 224 bytes long.
71        let mut data_with_hash = {
72            let mut buffer = Vec::with_capacity(224);
73            buffer.extend(&data_pad_reversed);
74            buffer.extend(sha256!(&temp_key, &data_with_padding));
75            buffer
76        };
77
78        // aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); -- AES256-IGE encryption with zero IV.
79        aes::ige_encrypt(data_with_hash.as_mut(), &temp_key, &[0u8; 32]);
80        let aes_encrypted = data_with_hash;
81
82        // temp_key_xor := temp_key XOR SHA256(aes_encrypted); -- adjusted key, 32 bytes
83        let temp_key_xor = {
84            let mut xored = temp_key;
85            xored
86                .iter_mut()
87                .zip(sha256!(&aes_encrypted))
88                .for_each(|(a, b)| *a ^= b);
89            xored
90        };
91
92        // key_aes_encrypted := temp_key_xor + aes_encrypted; -- exactly 256 bytes (2048 bits) long
93        let key_aes_encrypted = {
94            let mut buffer = Vec::with_capacity(256);
95            buffer.extend(temp_key_xor);
96            buffer.extend(aes_encrypted);
97            buffer
98        };
99
100        // The value of key_aes_encrypted is compared with the RSA-modulus of server_pubkey as a big-endian 2048-bit (256-byte) unsigned integer.
101        if BigUint::from_bytes_be(&key_aes_encrypted) >= key.n {
102            // If key_aes_encrypted turns out to be greater than or equal to the RSA modulus, the previous steps starting from the generation of new random temp_key are repeated.
103            increment(&mut temp_key);
104            continue;
105        }
106
107        // Otherwise the final step is performed:
108        break key_aes_encrypted;
109    };
110
111    // encrypted_data := RSA(key_aes_encrypted, server_pubkey); -- 256-byte big-endian integer is elevated to the requisite power from the RSA public key modulo the RSA modulus, and the result is stored as a big-endian integer consisting of exactly 256 bytes (with leading zero bytes if required).
112    let payload = BigUint::from_bytes_be(&key_aes_encrypted);
113    let encrypted = payload.modpow(&key.e, &key.n);
114    let mut block = encrypted.to_bytes_be();
115    while block.len() < 256 {
116        block.insert(0, 0);
117    }
118
119    block
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use crate::hex;
126
127    #[test]
128    fn test_rsa_encryption() {
129        let key = Key::new("25342889448840415564971689590713473206898847759084779052582026594546022463853940585885215951168491965708222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166848940585952337613333022396096584117954892216031229237302943701877588456738335398602461675225081791820393153757504952636234951323237820036543581047826906120927972487366805292115792231423684261262330394324750785450942589751755390156647751460719351439969059949569615302809050721500330239005077889855323917509948255722081644689442127297605422579707142646660768825302832201908302295573257427896031830742328565032949", "65537").unwrap();
130        let result = encrypt_hashed(
131            &hex::from_hex("955ff5a9081a8e635f5743de9b00000004453dc27100000004622f1fcb000000f7a81627bbf511fa4afef71e94a0937474586c1add9198dda81a5df8393871c8293623c5fb968894af1be7dfe9c7be813f9307789242fd0cb0c16a5cb39a8d3e"),
132            &key,
133            hex::from_hex("12270000635593b03fee033d0672f9afddf9124de9e77df6251806cba93482e4c9e6e06e7d44e4c4baae821aff91af44789689faaee9bdfc7b2df8c08709afe57396c4638ceaa0dc30114f82447e81d3b53edc423b32660c43a5b8ad057b64500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007dada0920c4973913229e0f881aec7b9db0c392d34f52fb0995ea493ecb4c09d").as_slice().try_into().unwrap(),
134        );
135        assert_eq!(
136            result,
137            hex::from_hex(
138                "b610642a828b4a61fe32931815cae318d311660580f1e0df768f3140f4d37dfcfcac0c2870318de4ff2d2e0e9669bcfdc0bad06cadb1b59d9726b427368a9c7b4fc0d5e7b2e99fc571968705c03acf5341fd7021bef653fa77b3776ae430e366fc46d232459ebe128b08d80e049ae579a48b56ca93b520709468587c81af96666046e9ea85091d729e921e8d8a36f57b27644052dae7387c7f4131701d59cda75251dac66c94276280ef950d3c44c21e5a2454f7da7a6818cf23ae9c490b72b2170d7cbc24f8a93db739d76f2d241c78b80123faaff3e664f074d6375d794dbf2800a0b5bb48d54eceafedfb355bfbebd287d9023264e3b53627888250787a9e"
139            )
140        );
141    }
142}