Esempio n. 1
0
def unpack_message(session, reader):
    """Unpacks a message following MtProto 2.0 guidelines"""
    # See https://core.telegram.org/mtproto/description
    if reader.read_long(signed=False) != session.auth_key.key_id:
        raise SecurityError('Server replied with an invalid auth key')

    msg_key = reader.read(16)
    aes_key, aes_iv = calc_key(session.auth_key.key, msg_key, False)
    data = BinaryReader(AES.decrypt_ige(reader.read(), aes_key, aes_iv))

    data.read_long()  # remote_salt
    if data.read_long() != session.id:
        raise SecurityError('Server replied with a wrong session ID')

    remote_msg_id = data.read_long()
    remote_sequence = data.read_int()
    msg_len = data.read_int()
    message = data.read(msg_len)

    # https://core.telegram.org/mtproto/security_guidelines
    # Sections "checking sha256 hash" and "message length"
    if msg_key != sha256(session.auth_key.key[96:96 + 32] +
                         data.get_bytes()).digest()[8:24]:
        raise SecurityError("Received msg_key doesn't match with expected one")

    return message, remote_msg_id, remote_sequence
    async def complete_rekey(self, peer,
                             action: DecryptedMessageActionCommitKey):
        peer = self.get_secret_chat(peer)
        if peer.rekeying[0] != 2 or self.temp_rekeyed_secret_chats.get(
                action.exchange_id, None):
            return
        if self.temp_rekeyed_secret_chats.get(
                action.exchange_id) != action.key_fingerprint:
            message = DecryptedMessageService(
                action=DecryptedMessageActionAbortKey(
                    exchange_id=action.exchange_id, ))
            message = await self.encrypt_secret_message(peer, message)
            await self.client(
                SendEncryptedServiceRequest(
                    InputEncryptedChat(peer.id, peer.access_hash), message))
            raise SecurityError("Invalid Key fingerprint")

        self._log.debug(f'Completing rekeying secret chat {peer}')
        peer.rekeying = [0]
        peer.auth_key = self.temp_rekeyed_secret_chats[action.exchange_id]
        peer.ttr = 100
        peer.updated = time()
        del self.temp_rekeyed_secret_chats[action.exchange_id]
        message = DecryptedMessageService(action=DecryptedMessageActionNoop())
        message = await self.encrypt_secret_message(peer, message)
        await self.client(
            SendEncryptedServiceRequest(
                InputEncryptedChat(peer.id, peer.access_hash), message))
        self._log.debug(f'Secret chat {peer} rekeyed succrsfully')
 async def commit_rekey(self, peer, action: DecryptedMessageActionAcceptKey):
     peer = self.get_secret_chat(peer)
     if peer.rekeying[0] != 1 or not self._temp_rekeyed_secret_chats.get(action.exchange_id, None):
         peer.rekeying = [0]
         return
     self._log.debug(f'Committing rekeying secret chat {peer}')
     dh_config = await self.get_dh_config()
     g_b = int.from_bytes(action.g_b, 'big', signed=False)
     self.check_g_a(g_b, dh_config.p)
     res = pow(g_b, self._temp_rekeyed_secret_chats[action.exchange_id], dh_config.p)
     auth_key = res.to_bytes(256, 'big', signed=False)
     key_fingerprint = struct.unpack('<q', sha1(auth_key).digest()[-8:])[0]
     if key_fingerprint != action.key_fingerprint:
         message = DecryptedMessageService(action=DecryptedMessageActionAbortKey(
             exchange_id=action.exchange_id,
         ))
         message = await self.encrypt_secret_message(peer, message)
         await self.client(SendEncryptedServiceRequest(InputEncryptedChat(peer.id, peer.access_hash), message))
         raise SecurityError("Invalid Key fingerprint")
     message = DecryptedMessageService(action=DecryptedMessageActionCommitKey(
         exchange_id=action.exchange_id,
         key_fingerprint=key_fingerprint
     ))
     message = await self.encrypt_secret_message(peer, message)
     await self.client(SendEncryptedServiceRequest(InputEncryptedChat(peer.id, peer.access_hash), message))
     del self._temp_rekeyed_secret_chats[action.exchange_id]
     peer.rekeying = [0]
     peer.auth_key = auth_key
     peer.ttl = 100
     peer.updated = time()
    def decrypt_mtproto1(self, message_key, chat_id, encrypted_data):
        aes_key, aes_iv = _old_calc_key(
            self.get_secret_chat(chat_id).auth_key, message_key, True)
        decrypted_data = AES.decrypt_ige(encrypted_data, aes_key, aes_iv)
        message_data_length = struct.unpack('<I', decrypted_data[:4])[0]
        message_data = decrypted_data[4:message_data_length + 4]

        if message_data_length > len(decrypted_data):
            raise SecurityError("message data length is too big")

        if message_key != sha1(
                decrypted_data[:4 + message_data_length]).digest()[-16:]:
            raise SecurityError("Message key mismatch")
        if len(decrypted_data) - 4 - message_data_length > 15:
            raise SecurityError("Difference is too big")
        if len(decrypted_data) % 16 != 0:
            raise SecurityError("Decrypted data can not be divided by 16")

        return BinaryReader(message_data).tgread_object()
    def decrypt_mtproto2(self, message_key, chat_id, encrypted_data):
        peer = self.get_secret_chat(chat_id)
        aes_key, aes_iv = MTProtoState._calc_key(peer.auth_key, message_key,
                                                 not peer.admin)

        decrypted_data = AES.decrypt_ige(encrypted_data, aes_key, aes_iv)
        message_data_length = struct.unpack('<I', decrypted_data[:4])[0]
        message_data = decrypted_data[4:message_data_length + 4]
        if message_data_length > len(decrypted_data):
            raise SecurityError("message data length is too big")
        is_admin = (8 if peer.admin else 0)
        first_str = peer.auth_key[88 + is_admin:88 + 32 + is_admin]

        if message_key != sha256(first_str + decrypted_data).digest()[8:24]:
            raise SecurityError("Message key mismatch")
        if len(decrypted_data) - 4 - message_data_length < 12:
            raise SecurityError("Padding is too small")
        if len(decrypted_data) % 16 != 0:
            raise SecurityError("Decrpyted data not divisble by 16")

        return BinaryReader(message_data).tgread_object()
    async def download_secret_media(self, message):
        if not message.file or not isinstance(message.file, EncryptedFile):
            return b""
        key_fingerprint = message.file.key_fingerprint
        key = message.media.key
        iv = message.media.iv
        digest = md5(key + iv).digest()

        fingerprint = int.from_bytes(digest[:4], byteorder="little", signed=True) ^ int.from_bytes(digest[4:8],
                                                                                                   byteorder="little",
                                                                                                   signed=True)
        if fingerprint != key_fingerprint:
            raise SecurityError("Wrong fingerprint")
        media = await self.client.download_file(InputEncryptedFileLocation(message.file.id, message.file.access_hash),
                                                key=message.media.key,
                                                iv=message.media.iv)
        return media