mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-10 19:46:36 +03:00
Fixed tiny bugs with authentication, added more unit tests
This commit is contained in:
parent
7c8c65560e
commit
b027dd2c8f
|
@ -36,6 +36,7 @@ class AES:
|
|||
def encrypt_ige(plain_text, key, iv):
|
||||
"""Encrypts the given text in 16-bytes blocks by using the given key and 32-bytes initialization vector"""
|
||||
# TODO: Random padding
|
||||
if len(plain_text) % 16 != 0: # Add padding if and only if it's not evenly divisible by 16 already
|
||||
padding = bytes(16 - len(plain_text) % 16)
|
||||
plain_text += padding
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# This file is based on TLSharp
|
||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/MtProtoPlainSender.cs
|
||||
import time
|
||||
import random
|
||||
from utils import BinaryWriter, BinaryReader
|
||||
|
||||
|
||||
|
@ -36,7 +37,11 @@ class MtProtoPlainSender:
|
|||
|
||||
def get_new_msg_id(self):
|
||||
"""Generates a new message ID based on the current time (in ms) since epoch"""
|
||||
new_msg_id = int(self._time_offset + time.time() * 1000) # Multiply by 1000 to get milliseconds
|
||||
# See https://core.telegram.org/mtproto/description#message-identifier-msg-id
|
||||
ms_time = int(time.time() * 1000)
|
||||
new_msg_id = (((ms_time // 1000) << 32) | # "must approximately equal unixtime*2^32"
|
||||
((ms_time % 1000) << 22) | # "approximate moment in time the message was created"
|
||||
random.randint(0, 524288) << 2) # "message identifiers are divisible by 4"
|
||||
|
||||
# Ensure that we always return a message ID which is higher than the previous one
|
||||
if self._last_msg_id >= new_msg_id:
|
||||
|
|
|
@ -100,7 +100,7 @@ class MtProtoSender:
|
|||
remote_auth_key_id = reader.read_long()
|
||||
msg_key = reader.read(16)
|
||||
|
||||
key, iv = utils.calc_key(self.session.auth_key.data, msg_key, False)
|
||||
key, iv = utils.calc_key(self.session.auth_key.key, msg_key, False)
|
||||
plain_text = AES.decrypt_ige(reader.read(len(body) - reader.tell_position()), key, iv)
|
||||
|
||||
with BinaryReader(plain_text) as plain_text_reader:
|
||||
|
@ -262,6 +262,8 @@ class MtProtoSender:
|
|||
|
||||
if request_id == mtproto_request.msg_id:
|
||||
mtproto_request.confirm_received = True
|
||||
else:
|
||||
print('We did not get the ID we expected. Got {}, but it should have been {}'.format(request_id, mtproto_request.msg_id))
|
||||
|
||||
inner_code = reader.read_int(signed=False)
|
||||
if inner_code == 0x2144ca19: # RPC Error
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# This file is based on TLSharp
|
||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpMessage.cs
|
||||
from zlib import crc32
|
||||
|
||||
from utils import BinaryWriter, BinaryReader
|
||||
from binascii import crc32
|
||||
|
||||
|
||||
class TcpMessage:
|
||||
|
@ -31,10 +30,8 @@ class TcpMessage:
|
|||
writer.write_int(len(self.body) + 12)
|
||||
writer.write_int(self.sequence_number)
|
||||
writer.write(self.body)
|
||||
writer.flush() # Flush so we can get the buffer in the CRC
|
||||
|
||||
# Ensure it's unsigned (see http://stackoverflow.com/a/30092291/4759433)
|
||||
crc = crc32(writer.get_bytes()[0:8 + len(self.body)]) & 0xFFFFFFFF
|
||||
crc = crc32(writer.get_bytes())
|
||||
writer.write_int(crc, signed=False)
|
||||
|
||||
return writer.get_bytes()
|
||||
|
@ -55,10 +52,9 @@ class TcpMessage:
|
|||
|
||||
seq = reader.read_int()
|
||||
packet = reader.read(packet_len - 12)
|
||||
checksum = reader.read_int()
|
||||
checksum = reader.read_int(signed=False)
|
||||
|
||||
# Ensure it's unsigned (see http://stackoverflow.com/a/30092291/4759433)
|
||||
valid_checksum = crc32(body[:packet_len - 4]) & 0xFFFFFFFF
|
||||
valid_checksum = crc32(body[:packet_len - 4])
|
||||
if checksum != valid_checksum:
|
||||
raise ValueError('Invalid checksum, skip')
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# This file is based on TLSharp
|
||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpTransport.cs
|
||||
from zlib import crc32
|
||||
from network import TcpMessage, TcpClient
|
||||
from binascii import crc32
|
||||
|
||||
|
||||
class TcpTransport:
|
||||
|
@ -35,12 +35,11 @@ class TcpTransport:
|
|||
|
||||
body = self._tcp_client.read(packet_length - 12)
|
||||
|
||||
checksum = int.from_bytes(self._tcp_client.read(4), byteorder='little')
|
||||
checksum = int.from_bytes(self._tcp_client.read(4), byteorder='little', signed=False)
|
||||
|
||||
# Then perform the checks
|
||||
rv = packet_length_bytes + seq_bytes + body
|
||||
# Ensure it's unsigned (http://stackoverflow.com/a/30092291/4759433)
|
||||
valid_checksum = crc32(rv) & 0xFFFFFFFF
|
||||
valid_checksum = crc32(rv)
|
||||
|
||||
if checksum != valid_checksum:
|
||||
raise ValueError('Invalid checksum, skip')
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import os
|
||||
# Only import most stuff if the TLObjects were generated
|
||||
if os.path.isfile('tl/all_tlobjects.py'):
|
||||
from .all_tlobjects import tlobjects
|
||||
from .session import Session
|
||||
from .mtproto_request import MTProtoRequest
|
||||
from .telegram_client import TelegramClient
|
||||
del os
|
||||
from .tlobject import TLObject, TLArg
|
||||
|
|
|
@ -4,6 +4,7 @@ from os.path import isfile as file_exists
|
|||
import time
|
||||
import pickle
|
||||
import utils
|
||||
import random
|
||||
|
||||
|
||||
class Session:
|
||||
|
@ -38,9 +39,13 @@ class Session:
|
|||
def get_new_msg_id(self):
|
||||
"""Generates a new message ID based on the current time (in ms) since epoch"""
|
||||
# Refer to mtproto_plain_sender.py for the original method, this is a simple copy
|
||||
new_msg_id = int(self.time_offset + time.time() * 1000)
|
||||
if self.last_message_id >= new_msg_id:
|
||||
new_msg_id = self._last_msg_id + 4
|
||||
ms_time = int(time.time() * 1000)
|
||||
new_msg_id = (((ms_time // 1000 + self.time_offset) << 32) | # "must approximately equal unixtime*2^32"
|
||||
((ms_time % 1000) << 22) | # "approximate moment in time the message was created"
|
||||
random.randint(0, 524288) << 2) # "message identifiers are divisible by 4"
|
||||
|
||||
self._last_msg_id = new_msg_id
|
||||
if self.last_message_id >= new_msg_id:
|
||||
new_msg_id = self.last_message_id + 4
|
||||
|
||||
self.last_message_id = new_msg_id
|
||||
return new_msg_id
|
||||
|
|
|
@ -2,8 +2,8 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
|
||||
from parser import TLParser
|
||||
from parser import SourceBuilder
|
||||
from parser.tl_parser import TLParser
|
||||
from parser.source_builder import SourceBuilder
|
||||
|
||||
|
||||
def tlobjects_exist():
|
||||
|
|
54
unit_test.py
54
unit_test.py
|
@ -5,16 +5,16 @@ import unittest
|
|||
import os
|
||||
import platform
|
||||
|
||||
import utils
|
||||
import network.authenticator
|
||||
from tl import Session
|
||||
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
||||
from tl.functions.help import GetConfigRequest
|
||||
|
||||
from crypto import AES, Factorizator
|
||||
from network import TcpTransport, TcpClient, MtProtoSender
|
||||
from utils import BinaryWriter, BinaryReader
|
||||
|
||||
from tl import Session
|
||||
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
||||
from tl.functions.help import GetConfigRequest
|
||||
import utils
|
||||
import network.authenticator
|
||||
|
||||
host = 'localhost'
|
||||
port = random.randint(50000, 60000) # Arbitrary non-privileged port
|
||||
|
@ -219,6 +219,50 @@ class UnitTest(unittest.TestCase):
|
|||
assert cipher_text == real, 'Decrypted text does not equal the real value (expected "{}", got "{}")'\
|
||||
.format(get_representation(real), get_representation(cipher_text))
|
||||
|
||||
@staticmethod
|
||||
def test_calc_key():
|
||||
shared_key = get_bytes('BC-D2-6D-B7-CA-76-F4-5D-5B-88-83-27-20-F3-11-8A-73-D0-34-94-31-AE-2A-4F-03-86-9A-2F-48-23-1A-8C-B5-6A-E9-24-E0-49-76-43-6D-5E-E7-30-1A-35-43-09-16-03-D2-9D-A9-89-D6-CE-08-50-0F-64-72-A0-B3-EB-FE-63-76-1A-DF-4A-14-96-98-16-A3-47-AB-04-14-21-5C-EB-0A-BC-6E-DF-C4-25-C6-09-B7-16-14-9C-27-81-15-3D-B0-AF-0E-0B-52-AA-04-36-36-73-F0-CF-B7-B8-3E-2C-44-94-78-D7-F8-E0-84-CB-25-D3-05-B2-E8-95-4D-72-3F-A2-E8-49-6E-F9-0B-5B-45-9B-AA-0C-58-7F-0E-69-DE-EE-64-1D-78-2F-4A-CE-EA-5E-7D-30-3B-A8-33-42-BB-52-A1-BF-65-04-B9-1E-A1-22-66-3D-A5-4D-40-9E-DD-81-80-C9-A5-FB-FC-67-DD-15-03-70-21-0F-66-44-16-89-32-EA-CA-B1-41-99-4F-A9-34-50-A9-A2-C6-3B-B2-43-39-1D-43-35-D2-0D-EC-4C-D9-AB-77-2D-03-0D-79-C2-76-17-5D-02-15-0C-42-61-97-CE-A5-B1-E4-5D-8E-E0-2C-CF-43-7B-6F-FA-99-66-A4-70-4D-00')
|
||||
msg_keys = [
|
||||
'BA-1A-CF-DA-A8-5E-43-62-6C-FA-B6-0C-3A-9B-B0-FC',
|
||||
'86-6D-92-69-CF-8B-93-AA-86-4B-1F-69-D0-34-83-5D',
|
||||
'A1-2F-C0-61-6F-60-1A-B0-33-8F-7D-27-08-C8-EA-15',
|
||||
'3A-56-52-0F-B7-89-D7-80-F6-18-72-CD-09-B5-A8-8A',
|
||||
'06-F7-84-F3-91-CC-8D-DC-7D-92-41-7A-7E-84-25-E4',
|
||||
'78-4D-FC-AE-F4-C4-55-81-6D-DD-99-A7-DB-B8-A3-88'
|
||||
]
|
||||
are_client = [
|
||||
True,
|
||||
False,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False
|
||||
]
|
||||
|
||||
valid_keys = [
|
||||
'AF-E3-84-51-6D-E0-21-0C-D9-31-E4-9A-A0-76-5F-67-63-78-A1-B0-C9-BC-16-27-76-CF-2C-9D-4D-AE-C6-A5',
|
||||
'DD-30-58-B6-93-8E-C9-79-EF-83-F8-8C-6A-A7-68-03-E2-C6-B1-36-C5-BB-FC-E7-DF-D6-B1-67-F7-75-CF-6B',
|
||||
'70-3F-66-AF-FE-0A-F3-1E-95-C3-25-48-8D-0F-A7-95-59-53-BF-DD-35-97-6E-7A-C0-5E-79-9C-9D-09-3B-7B',
|
||||
'20-9F-82-E3-95-3F-9D-1E-EE-3E-F3-82-B0-8D-6E-76-26-5B-94-27-DD-7D-61-C3-AC-EB-FA-71-FF-0D-8F-08',
|
||||
'AE-06-BF-F1-89-88-22-66-98-48-76-E6-BD-D9-39-36-2D-36-4E-CF-FD-47-D2-87-D7-49-F8-93-22-2E-66-02',
|
||||
'D9-6B-43-D0-89-F7-C5-75-A2-4A-F2-2F-F0-17-5C-95-AE-FA-46-A5-95-AA-C3-B9-76-B0-3A-A8-0E-7B-EA-5D'
|
||||
]
|
||||
|
||||
valid_ivs = [
|
||||
'B8-51-F3-C5-A3-5D-C6-DF-9E-E0-51-BD-22-8D-13-09-0E-9A-9D-5E-38-A2-F8-E7-00-77-D9-C1-A7-A0-F7-0F',
|
||||
'DC-4C-C2-18-01-4A-22-58-86-6C-62-B6-B5-34-37-FD-E2-61-34-B6-AF-7D-46-53-D7-5B-E0-4E-0D-19-FB-BC',
|
||||
'68-BB-BA-7F-55-B9-EF-86-EE-20-5A-1A-45-4E-70-C3-48-56-A2-E9-2F-91-AD-74-23-FE-54-06-E5-68-04-E9',
|
||||
'79-31-8D-F0-DC-31-60-D7-09-BC-66-F1-AB-0D-7C-CB-2A-AF-74-32-64-C5-B3-18-C4-ED-55-D9-F6-39-DD-F3',
|
||||
'1F-51-66-06-05-54-B9-E0-52-6A-88-6A-70-0C-DA-8D-2B-CD-BF-8A-67-5E-1A-A7-DD-EA-1C-CE-4C-D4-34-3D',
|
||||
'8E-00-97-5E-B7-A1-F7-3D-1C-16-03-CA-B3-ED-EA-80-64-8F-77-A6-C4-34-5B-B5-DC-5D-C9-EC-B7-F8-F4-76'
|
||||
]
|
||||
|
||||
for msg_key, is_client, valid_key, valid_iv in zip(msg_keys, are_client, valid_keys, valid_ivs):
|
||||
msg_key = get_bytes(msg_key)
|
||||
key, iv = utils.calc_key(shared_key, msg_key, is_client)
|
||||
assert get_representation(key) == valid_key
|
||||
assert get_representation(iv) == valid_iv
|
||||
|
||||
@staticmethod
|
||||
def test_authenticator():
|
||||
transport = TcpTransport('149.154.167.91', 443)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from .binary_writer import BinaryWriter
|
||||
from .binary_reader import BinaryReader
|
||||
from .binary_writer import BinaryWriter
|
||||
from .helpers import *
|
||||
|
|
|
@ -84,6 +84,16 @@ class BinaryReader:
|
|||
"""Reads a Telegram-encoded string"""
|
||||
return str(self.tgread_bytes(), encoding='utf-8')
|
||||
|
||||
def tgread_bool(self):
|
||||
"""Reads a Telegram boolean value"""
|
||||
value = self.read_int(signed=False)
|
||||
if value == 0x997275b5: # boolTrue
|
||||
return True
|
||||
elif value == 0xbc799737: # boolFalse
|
||||
return False
|
||||
else:
|
||||
raise ValueError('Invalid boolean code {}'.format(hex(value)))
|
||||
|
||||
def tgread_object(self):
|
||||
"""Reads a Telegram object"""
|
||||
constructor_id = self.read_int()
|
||||
|
|
Loading…
Reference in New Issue
Block a user