mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2026-09-09 18:26:19 +00:00
Several fixes to authenticator, added more unit tests
Some fixes include, in more detail: - Using little over big endianess in some parts - Flagging all the constructor numbers as unsigned - Fixed bugs with factorizer - Implemented TLSharp's RSA
This commit is contained in:
Regular → Executable
Regular → Executable
+19
-14
@@ -12,7 +12,7 @@ from utils.auth_key import AuthKey
|
||||
import utils.helpers as utils
|
||||
import time
|
||||
import pyaes
|
||||
import rsa
|
||||
from utils.rsa import RSA
|
||||
|
||||
|
||||
def do_authentication(transport):
|
||||
@@ -21,7 +21,7 @@ def do_authentication(transport):
|
||||
# Step 1 sending: PQ Request
|
||||
nonce = utils.generate_random_bytes(16)
|
||||
with BinaryWriter() as writer:
|
||||
writer.write_int(0x60469778) # Constructor number
|
||||
writer.write_int(0x60469778, signed=False) # Constructor number
|
||||
writer.write(nonce)
|
||||
sender.send(writer.get_bytes())
|
||||
|
||||
@@ -39,7 +39,7 @@ def do_authentication(transport):
|
||||
server_nonce = reader.read(16)
|
||||
|
||||
pq_bytes = reader.tgread_bytes()
|
||||
pq = int.from_bytes(pq_bytes, byteorder='big')
|
||||
pq = int.from_bytes(pq_bytes, byteorder='little')
|
||||
|
||||
vector_id = reader.read_int()
|
||||
if vector_id != 0x1cb5c415:
|
||||
@@ -54,7 +54,7 @@ def do_authentication(transport):
|
||||
new_nonce = utils.generate_random_bytes(32)
|
||||
p, q = Factorizator.factorize(pq)
|
||||
with BinaryWriter() as pq_inner_data_writer:
|
||||
pq_inner_data_writer.write_int(0x83c95aec) # PQ Inner Data
|
||||
pq_inner_data_writer.write_int(0x83c95aec, signed=False) # PQ Inner Data
|
||||
pq_inner_data_writer.tgwrite_bytes(utils.get_byte_array(pq, signed=False))
|
||||
pq_inner_data_writer.tgwrite_bytes(utils.get_byte_array(min(p, q), signed=False))
|
||||
pq_inner_data_writer.tgwrite_bytes(utils.get_byte_array(max(p, q), signed=False))
|
||||
@@ -64,8 +64,7 @@ def do_authentication(transport):
|
||||
|
||||
cipher_text, target_fingerprint = None, None
|
||||
for fingerprint in fingerprints:
|
||||
cipher_text = rsa.encrypt(str(fingerprint, encoding='utf-8').replace('-', ''),
|
||||
pq_inner_data_writer.get_bytes())
|
||||
cipher_text = RSA.encrypt(get_fingerprint_text(fingerprint), pq_inner_data_writer.get_bytes())
|
||||
|
||||
if cipher_text is not None:
|
||||
target_fingerprint = fingerprint
|
||||
@@ -73,15 +72,15 @@ def do_authentication(transport):
|
||||
|
||||
if cipher_text is None:
|
||||
raise AssertionError('Could not find a valid key for fingerprints: {}'
|
||||
.format(', '.join([str(f, encoding='utf-8') for f in fingerprints])))
|
||||
.format(', '.join([get_fingerprint_text(f) for f in fingerprints])))
|
||||
|
||||
with BinaryWriter() as req_dh_params_writer:
|
||||
req_dh_params_writer.write_int(0xd712e4be) # Req DH Params
|
||||
req_dh_params_writer.write_int(0xd712e4be, signed=False) # Req DH Params
|
||||
req_dh_params_writer.write(nonce)
|
||||
req_dh_params_writer.write(server_nonce)
|
||||
req_dh_params_writer.tgwrite_bytes(utils.get_byte_array(min(p, q), signed=False))
|
||||
req_dh_params_writer.tgwrite_bytes(utils.get_byte_array(max(p, q), signed=False))
|
||||
req_dh_params_writer.Write(target_fingerprint)
|
||||
req_dh_params_writer.write(target_fingerprint)
|
||||
req_dh_params_writer.tgwrite_bytes(cipher_text)
|
||||
|
||||
req_dh_params_bytes = req_dh_params_writer.get_bytes()
|
||||
@@ -89,6 +88,7 @@ def do_authentication(transport):
|
||||
|
||||
# Step 2 response: DH Exchange
|
||||
encrypted_answer = None
|
||||
# TODO, there is no data to read? What's going on?
|
||||
with BinaryReader(sender.receive()) as reader:
|
||||
response_code = reader.read_int(signed=False)
|
||||
|
||||
@@ -129,19 +129,19 @@ def do_authentication(transport):
|
||||
raise AssertionError('Invalid server nonce in encrypted answer')
|
||||
|
||||
g = dh_inner_data_reader.read_int()
|
||||
dh_prime = int.from_bytes(dh_inner_data_reader.tgread_bytes(), byteorder='big', signed=True)
|
||||
ga = int.from_bytes(dh_inner_data_reader.tgread_bytes(), byteorder='big', signed=True)
|
||||
dh_prime = int.from_bytes(dh_inner_data_reader.tgread_bytes(), byteorder='little', signed=True)
|
||||
ga = int.from_bytes(dh_inner_data_reader.tgread_bytes(), byteorder='little', signed=True)
|
||||
|
||||
server_time = dh_inner_data_reader.read_int()
|
||||
time_offset = server_time - int(time.time() * 1000) # Multiply by 1000 to get milliseconds
|
||||
|
||||
b = int.from_bytes(utils.generate_random_bytes(2048), byteorder='big')
|
||||
b = int.from_bytes(utils.generate_random_bytes(2048), byteorder='little')
|
||||
gb = pow(g, b, dh_prime)
|
||||
gab = pow(ga, b, dh_prime)
|
||||
|
||||
# Prepare client DH Inner Data
|
||||
with BinaryWriter() as client_dh_inner_data_writer:
|
||||
client_dh_inner_data_writer.write_int(0x6643b654) # Client DH Inner Data
|
||||
client_dh_inner_data_writer.write_int(0x6643b654, signed=False) # Client DH Inner Data
|
||||
client_dh_inner_data_writer.write(nonce)
|
||||
client_dh_inner_data_writer.write(server_nonce)
|
||||
client_dh_inner_data_writer.write_long(0) # TODO retry_id
|
||||
@@ -157,7 +157,7 @@ def do_authentication(transport):
|
||||
|
||||
# Prepare Set client DH params
|
||||
with BinaryWriter() as set_client_dh_params_writer:
|
||||
set_client_dh_params_writer.write_int(0xf5045f1f)
|
||||
set_client_dh_params_writer.write_int(0xf5045f1f, signed=False)
|
||||
set_client_dh_params_writer.write(nonce)
|
||||
set_client_dh_params_writer.write(server_nonce)
|
||||
set_client_dh_params_writer.tgwrite_bytes(client_dh_inner_data_encrypted_bytes)
|
||||
@@ -194,3 +194,8 @@ def do_authentication(transport):
|
||||
|
||||
else:
|
||||
raise AssertionError('DH Gen unknown: {}'.format(hex(code)))
|
||||
|
||||
|
||||
def get_fingerprint_text(fingerprint):
|
||||
"""Gets a fingerprint text in 01-23-45-67-89-AB-CD-EF format (no hyphens)"""
|
||||
return ''.join(hex(b)[2:].rjust(2, '0').upper() for b in fingerprint)
|
||||
Regular → Executable
+1
-1
@@ -17,7 +17,7 @@ class MtProtoPlainSender:
|
||||
"""Sends a plain packet (auth_key_id = 0) containing the given message body (data)"""
|
||||
with BinaryWriter() as writer:
|
||||
writer.write_long(0)
|
||||
writer.write_int(self.get_new_msg_id())
|
||||
writer.write_long(self.get_new_msg_id())
|
||||
writer.write_int(len(data))
|
||||
writer.write(data)
|
||||
|
||||
|
||||
Regular → Executable
Regular → Executable
+2
@@ -10,10 +10,12 @@ class TcpClient:
|
||||
def connect(self, ip, port):
|
||||
"""Connects to the specified IP and port number"""
|
||||
self.socket.connect((ip, port))
|
||||
self.connected = True
|
||||
|
||||
def close(self):
|
||||
"""Closes the connection"""
|
||||
self.socket.close()
|
||||
self.connected = False
|
||||
|
||||
def write(self, data):
|
||||
"""Writes (sends) the specified bytes to the connected peer"""
|
||||
|
||||
Regular → Executable
+5
-3
@@ -34,7 +34,8 @@ class TcpMessage:
|
||||
writer.write(self.body)
|
||||
writer.flush() # Flush so we can get the buffer in the CRC
|
||||
|
||||
crc = crc32(writer.get_bytes()[0:8 + len(self.body)])
|
||||
# Ensure it's unsigned (see http://stackoverflow.com/a/30092291/4759433)
|
||||
crc = crc32(writer.get_bytes()[0:8 + len(self.body)]) & 0xFFFFFFFF
|
||||
writer.write_int(crc, signed=False)
|
||||
|
||||
return writer.get_bytes()
|
||||
@@ -49,7 +50,7 @@ class TcpMessage:
|
||||
raise ValueError('Wrong size of input packet')
|
||||
|
||||
with BinaryReader(body) as reader:
|
||||
packet_len = int.from_bytes(reader.read(4), byteorder='big')
|
||||
packet_len = int.from_bytes(reader.read(4), byteorder='little')
|
||||
if packet_len < 12:
|
||||
raise ValueError('Invalid packet length: {}'.format(packet_len))
|
||||
|
||||
@@ -57,7 +58,8 @@ class TcpMessage:
|
||||
packet = reader.read(packet_len - 12)
|
||||
checksum = reader.read_int()
|
||||
|
||||
valid_checksum = crc32(body[:packet_len - 4])
|
||||
# Ensure it's unsigned (see http://stackoverflow.com/a/30092291/4759433)
|
||||
valid_checksum = crc32(body[:packet_len - 4]) & 0xFFFFFFFF
|
||||
if checksum != valid_checksum:
|
||||
raise ValueError('Invalid checksum, skip')
|
||||
|
||||
|
||||
Regular → Executable
+5
-4
@@ -29,18 +29,19 @@ class TcpTransport:
|
||||
|
||||
# First read everything
|
||||
packet_length_bytes = self._tcp_client.read(4)
|
||||
packet_length = int.from_bytes(packet_length_bytes, byteorder='big')
|
||||
packet_length = int.from_bytes(packet_length_bytes, byteorder='little')
|
||||
|
||||
seq_bytes = self._tcp_client.read(4)
|
||||
seq = int.from_bytes(seq_bytes, byteorder='big')
|
||||
seq = int.from_bytes(seq_bytes, byteorder='little')
|
||||
|
||||
body = self._tcp_client.read(packet_length - 12)
|
||||
|
||||
checksum = int.from_bytes(self._tcp_client.read(4), byteorder='big')
|
||||
checksum = int.from_bytes(self._tcp_client.read(4), byteorder='little')
|
||||
|
||||
# Then perform the checks
|
||||
rv = packet_length_bytes + seq_bytes + body
|
||||
valid_checksum = crc32(rv) & 0xFFFFFFFF # Ensure it's unsigned (http://stackoverflow.com/a/30092291/4759433)
|
||||
# Ensure it's unsigned (http://stackoverflow.com/a/30092291/4759433)
|
||||
valid_checksum = crc32(rv) & 0xFFFFFFFF
|
||||
|
||||
if checksum != valid_checksum:
|
||||
raise ValueError('Invalid checksum, skip')
|
||||
|
||||
Reference in New Issue
Block a user