use aes::Aes256;
use cipher::{KeyIvInit, StreamCipher};
use ctr::Ctr128BE;
use rand::RngCore;
use sha2::{Digest, Sha256};
pub type AesCtr256 = Ctr128BE<Aes256>;
pub const HANDSHAKE_LEN: usize = 64;
pub const SKIP_LEN: usize = 8; const PREKEY_LEN: usize = 32;
const IV_LEN: usize = 16;
pub const PROTO_TAG_POS: usize = 56;
pub const DC_IDX_POS: usize = 60;
pub const PROTO_TAG_ABRIDGED: [u8; 4] = [0xef, 0xef, 0xef, 0xef];
pub const PROTO_TAG_INTERMEDIATE: [u8; 4] = [0xee, 0xee, 0xee, 0xee];
pub const PROTO_TAG_SECURE: [u8; 4] = [0xdd, 0xdd, 0xdd, 0xdd];
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ProtoTag {
Abridged,
Intermediate,
PaddedIntermediate,
}
impl ProtoTag {
pub fn from_bytes(b: &[u8]) -> Option<Self> {
match b {
x if x == PROTO_TAG_ABRIDGED => Some(Self::Abridged),
x if x == PROTO_TAG_INTERMEDIATE => Some(Self::Intermediate),
x if x == PROTO_TAG_SECURE => Some(Self::PaddedIntermediate),
_ => None,
}
}
pub fn as_bytes(&self) -> [u8; 4] {
match self {
Self::Abridged => PROTO_TAG_ABRIDGED,
Self::Intermediate => PROTO_TAG_INTERMEDIATE,
Self::PaddedIntermediate => PROTO_TAG_SECURE,
}
}
}
pub struct HandshakeInfo {
pub dc_id: u32,
pub is_media: bool,
pub proto: ProtoTag,
pub prekey_and_iv: [u8; PREKEY_LEN + IV_LEN],
}
pub fn parse_handshake(handshake: &[u8; HANDSHAKE_LEN], secret: &[u8]) -> Option<HandshakeInfo> {
let prekey = &handshake[SKIP_LEN..SKIP_LEN + PREKEY_LEN];
let iv = &handshake[SKIP_LEN + PREKEY_LEN..SKIP_LEN + PREKEY_LEN + IV_LEN];
let key = {
let mut h = Sha256::new();
h.update(prekey);
h.update(secret);
h.finalize()
};
let mut buf = *handshake;
let mut cipher = make_cipher(&key, iv);
cipher.apply_keystream(&mut buf);
let proto = ProtoTag::from_bytes(&buf[PROTO_TAG_POS..PROTO_TAG_POS + 4])?;
let dc_idx = i16::from_le_bytes([buf[DC_IDX_POS], buf[DC_IDX_POS + 1]]);
let dc_id = dc_idx.unsigned_abs() as u32;
let is_media = dc_idx < 0;
let prekey_and_iv: [u8; PREKEY_LEN + IV_LEN] = handshake[SKIP_LEN..SKIP_LEN + PREKEY_LEN + IV_LEN]
.try_into()
.unwrap();
Some(HandshakeInfo {
dc_id,
is_media,
proto,
prekey_and_iv,
})
}
const RESERVED_FIRST_BYTES: &[u8] = &[0xef];
const RESERVED_STARTS: &[[u8; 4]] = &[
[0x48, 0x45, 0x41, 0x44], [0x50, 0x4f, 0x53, 0x54], [0x47, 0x45, 0x54, 0x20], [0xee, 0xee, 0xee, 0xee],
[0xdd, 0xdd, 0xdd, 0xdd],
[0x16, 0x03, 0x01, 0x02], ];
const RESERVED_CONTINUE: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
pub fn generate_relay_init(proto: ProtoTag, dc_idx: i16) -> [u8; HANDSHAKE_LEN] {
let proto_tag = proto.as_bytes();
let dc_bytes = dc_idx.to_le_bytes();
loop {
let mut rnd = [0u8; HANDSHAKE_LEN];
rand::thread_rng().fill_bytes(&mut rnd);
if RESERVED_FIRST_BYTES.contains(&rnd[0]) {
continue;
}
if RESERVED_STARTS.iter().any(|s| &rnd[..4] == s) {
continue;
}
if rnd[4..8] == RESERVED_CONTINUE {
continue;
}
let enc_key = &rnd[SKIP_LEN..SKIP_LEN + PREKEY_LEN];
let enc_iv = &rnd[SKIP_LEN + PREKEY_LEN..SKIP_LEN + PREKEY_LEN + IV_LEN];
let mut cipher = make_cipher(enc_key, enc_iv);
let mut encrypted = rnd;
cipher.apply_keystream(&mut encrypted);
let mut tail_plain = [0u8; 8];
tail_plain[..4].copy_from_slice(&proto_tag);
tail_plain[4..6].copy_from_slice(&dc_bytes);
rand::thread_rng().fill_bytes(&mut tail_plain[6..]);
let mut result = rnd;
for i in 0..8 {
let ks = encrypted[PROTO_TAG_POS + i] ^ rnd[PROTO_TAG_POS + i];
result[PROTO_TAG_POS + i] = tail_plain[i] ^ ks;
}
return result;
}
}
pub fn make_cipher(key: &[u8], iv: &[u8]) -> AesCtr256 {
AesCtr256::new_from_slices(key, iv).expect("key must be 32 bytes and iv must be 16 bytes")
}
pub struct ConnectionCiphers {
pub clt_dec: AesCtr256,
pub clt_enc: AesCtr256,
pub tg_enc: AesCtr256,
pub tg_dec: AesCtr256,
}
pub fn build_connection_ciphers(
prekey_and_iv: &[u8; PREKEY_LEN + IV_LEN],
secret: &[u8],
relay_init: &[u8; HANDSHAKE_LEN],
) -> ConnectionCiphers {
let clt_dec_key = {
let mut h = Sha256::new();
h.update(&prekey_and_iv[..PREKEY_LEN]);
h.update(secret);
h.finalize()
};
let clt_dec_iv = &prekey_and_iv[PREKEY_LEN..];
let reversed: Vec<u8> = prekey_and_iv.iter().rev().copied().collect();
let clt_enc_key = {
let mut h = Sha256::new();
h.update(&reversed[..PREKEY_LEN]);
h.update(secret);
h.finalize()
};
let clt_enc_iv = &reversed[PREKEY_LEN..];
let mut clt_dec = make_cipher(&clt_dec_key, clt_dec_iv);
let clt_enc = make_cipher(&clt_enc_key, clt_enc_iv);
let mut dummy = [0u8; HANDSHAKE_LEN];
clt_dec.apply_keystream(&mut dummy);
let relay_enc_key = &relay_init[SKIP_LEN..SKIP_LEN + PREKEY_LEN];
let relay_enc_iv = &relay_init[SKIP_LEN + PREKEY_LEN..SKIP_LEN + PREKEY_LEN + IV_LEN];
let relay_prekey_iv_rev: Vec<u8> = relay_init[SKIP_LEN..SKIP_LEN + PREKEY_LEN + IV_LEN]
.iter()
.rev()
.copied()
.collect();
let relay_dec_key = &relay_prekey_iv_rev[..PREKEY_LEN];
let relay_dec_iv = &relay_prekey_iv_rev[PREKEY_LEN..];
let mut tg_enc = make_cipher(relay_enc_key, relay_enc_iv);
let tg_dec = make_cipher(relay_dec_key, relay_dec_iv);
tg_enc.apply_keystream(&mut dummy);
ConnectionCiphers {
clt_dec,
clt_enc,
tg_enc,
tg_dec,
}
}