1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
// Copyright 2020 - developers of the `grammers` project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::errors::DeserializeError;
use grammers_tl_types::{Cursor, Deserializable, Serializable};

/// An implementation of the [Mobile Transport Protocol] for plaintext
/// (unencrypted) messages.
///
/// The reason to separate the plaintext and encrypted implementations
/// for serializing messages is that, even though they are similar, the
/// benefits outweight some minor code reuse.
///
/// This way, the encryption key for [`Mtp`] is mandatory so errors
/// for trying to encrypt data without a key are completely eliminated.
///
/// Also, the plaintext part of the protocol does not need to deal with
/// the complexity of the full protocol once encrypted messages are used,
/// so being able to keep a simpler implementation separate is a bonus.
///
/// [Mobile Transport Protocol]: https://core.telegram.org/mtproto
/// [`Mtp`]: struct.Mtp.html
#[non_exhaustive]
pub struct PlainMtp;

impl PlainMtp {
    pub fn new() -> Self {
        Self
    }

    /// Wraps a request's data into a plain message (also known as
    /// [unencrypted messages]), and returns its serialized contents.
    ///
    /// Plain messages may be used for requests that don't require an
    /// authorization key to be present, such as those needed to generate
    /// the authorization key itself.
    ///
    /// [unencrypted messages]: https://core.telegram.org/mtproto/description#unencrypted-message
    pub fn serialize_plain_message(&mut self, body: &[u8]) -> Vec<u8> {
        let mut buf = Vec::with_capacity(body.len() + 8 + 8 + 4);
        0i64.serialize(&mut buf);

        // Even though https://core.telegram.org/mtproto/samples-auth_key
        // seems to imply the `msg_id` has to follow some rules, there is
        // no need to generate a valid `msg_id`, it seems. Just use `0`.
        0i64.serialize(&mut buf);

        (body.len() as u32).serialize(&mut buf);
        buf.extend(body);
        buf
    }

    /// Validates that the returned data is a correct plain message, and
    /// if it is, the method returns the inner contents of the message.
    ///
    /// [`serialize_plain_message`]: #method.serialize_plain_message
    pub fn deserialize_plain_message<'a>(
        &self,
        message: &'a [u8],
    ) -> Result<&'a [u8], DeserializeError> {
        crate::utils::check_message_buffer(message)?;

        let mut buf = Cursor::from_slice(message);
        let auth_key_id = i64::deserialize(&mut buf)?;
        if auth_key_id != 0 {
            return Err(DeserializeError::BadAuthKey {
                got: auth_key_id,
                expected: 0,
            });
        }

        let msg_id = i64::deserialize(&mut buf)?;
        // We can't validate it's close to our system time because our sytem
        // time may be wrong at this point (it only matters once encrypted
        // communication begins). However, we can validate the following:
        //
        // > server message identifiers modulo 4 yield 1 if
        // > the message is a response to a client message
        // https://core.telegram.org/mtproto/description#message-identifier-msg-id
        if msg_id <= 0 || (msg_id % 4) != 1 {
            return Err(DeserializeError::BadMessageId { got: msg_id });
        }

        let len = i32::deserialize(&mut buf)?;
        if len <= 0 {
            return Err(DeserializeError::NegativeMessageLength { got: len });
        }
        if (20 + len) as usize > message.len() {
            return Err(DeserializeError::TooLongMessageLength {
                got: len as usize,
                max_length: message.len() - 20,
            });
        }

        Ok(&message[20..20 + len as usize])
    }
}