Could not extract type: vector<%Message>
This commit is contained in:
parent
de05aad138
commit
32b99a298c
7
TL.py
7
TL.py
@ -3,7 +3,7 @@ import os
|
|||||||
import struct
|
import struct
|
||||||
import json
|
import json
|
||||||
import io
|
import io
|
||||||
import numbers
|
from numbers import Number
|
||||||
|
|
||||||
class TlConstructor:
|
class TlConstructor:
|
||||||
def __init__(self, json_dict):
|
def __init__(self, json_dict):
|
||||||
@ -78,10 +78,11 @@ def serialize_method(bytes_io, type_, **kwargs):
|
|||||||
|
|
||||||
def serialize_param(bytes_io, type_, value):
|
def serialize_param(bytes_io, type_, value):
|
||||||
if type_ == "int":
|
if type_ == "int":
|
||||||
assert isinstance(value, int)
|
assert isinstance(value, Number)
|
||||||
|
assert value.bit_length() <= 32
|
||||||
bytes_io.write(struct.pack('<i', value))
|
bytes_io.write(struct.pack('<i', value))
|
||||||
elif type_ == "long":
|
elif type_ == "long":
|
||||||
assert isinstance(value, numbers.Real)
|
assert isinstance(value, Number)
|
||||||
bytes_io.write(struct.pack('<q', value))
|
bytes_io.write(struct.pack('<q', value))
|
||||||
elif type_ in ["int128", "int256"]:
|
elif type_ in ["int128", "int256"]:
|
||||||
assert isinstance(value, bytes)
|
assert isinstance(value, bytes)
|
||||||
|
3
crypt.py
3
crypt.py
@ -27,7 +27,8 @@ def _ige(message, key, iv, operation="decrypt"):
|
|||||||
key must be 32 byte
|
key must be 32 byte
|
||||||
iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
|
iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
|
||||||
needed for IGE)"""
|
needed for IGE)"""
|
||||||
|
if not isinstance(message, str):
|
||||||
|
message = str(message)
|
||||||
if len(key) != 32:
|
if len(key) != 32:
|
||||||
raise ValueError("key must be 32 bytes long (was " +
|
raise ValueError("key must be 32 bytes long (was " +
|
||||||
str(len(key)) + " bytes)")
|
str(len(key)) + " bytes)")
|
||||||
|
130
mtproto.py
130
mtproto.py
@ -6,12 +6,9 @@ Created on Tue Sep 2 19:26:15 2014
|
|||||||
@author: Sammy Pfeiffer
|
@author: Sammy Pfeiffer
|
||||||
"""
|
"""
|
||||||
from binascii import crc32 as originalcrc32
|
from binascii import crc32 as originalcrc32
|
||||||
import numbers
|
|
||||||
from datetime import datetime
|
|
||||||
from time import time
|
from time import time
|
||||||
import io
|
import io
|
||||||
import os.path
|
import os.path
|
||||||
import json
|
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
@ -46,64 +43,52 @@ def vis(bs):
|
|||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
""" Manages TCP Transport. encryption and message frames """
|
""" Manages TCP Transport. encryption and message frames """
|
||||||
def __init__(self, ip, port, auth_key=None):
|
def __init__(self, ip, port, auth_key=None, server_salt=None):
|
||||||
# creating socket
|
# creating socket
|
||||||
self.sock = socket.socket()
|
self.sock = socket.socket()
|
||||||
self.sock.connect((ip, port))
|
self.sock.connect((ip, port))
|
||||||
self.number = 0
|
self.number = 0
|
||||||
|
self.timedelta = 0
|
||||||
if auth_key is None:
|
self.session_id = os.urandom(8)
|
||||||
self.create_auth_key()
|
self.auth_key = auth_key
|
||||||
else:
|
self.auth_key_id = SHA.new(self.auth_key).digest()[-8:] if self.auth_key else None
|
||||||
self.auth_key = auth_key
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
# closing socket when session object is deleted
|
# closing socket when session object is deleted
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
|
|
||||||
@staticmethod
|
def send_message(self, message_data):
|
||||||
def header_unencrypted(message):
|
|
||||||
"""
|
|
||||||
Creating header for the unencrypted message:
|
|
||||||
:param message: byte string to send
|
|
||||||
"""
|
|
||||||
# Basic instructions: https://core.telegram.org/mtproto/description#unencrypted-message
|
|
||||||
|
|
||||||
# Message id: https://core.telegram.org/mtproto/description#message-identifier-msg-id
|
|
||||||
# http://stackoverflow.com/questions/8777753/converting-datetime-date-to-utc-timestamp-in-python
|
|
||||||
# to make it work in py2 and py3 (py3 has the timestamp() method but py2 doesnt)
|
|
||||||
#curr_timestamp = (datetime.utcfromtimestamp(time()) - datetime(1970, 1, 1)).total_seconds()
|
|
||||||
|
|
||||||
msg_id = int(time()*2**30)*4
|
|
||||||
|
|
||||||
#msg_id = int(datetime.utcnow().timestamp()*2**30)*4
|
|
||||||
|
|
||||||
return (b'\x00\x00\x00\x00\x00\x00\x00\x00' +
|
|
||||||
struct.pack('<Q', msg_id) +
|
|
||||||
struct.pack('<I', len(message)))
|
|
||||||
|
|
||||||
# TCP Transport
|
|
||||||
|
|
||||||
# Instructions may be found here: https://core.telegram.org/mtproto#tcp-transport
|
|
||||||
# If a payload (packet) needs to be transmitted from server to client or from client to server,
|
|
||||||
# it is encapsulated as follows: 4 length bytes are added at the front (to include the length,
|
|
||||||
# the sequence number, and CRC32; always divisible by 4) and 4 bytes with the packet sequence number
|
|
||||||
# within this TCP connection (the first packet sent is numbered 0, the next one 1, etc.),
|
|
||||||
# and 4 CRC32 bytes at the end (length, sequence number, and payload together).
|
|
||||||
|
|
||||||
def send_message(self, message):
|
|
||||||
"""
|
"""
|
||||||
Forming the message frame and sending message to server
|
Forming the message frame and sending message to server
|
||||||
:param message: byte string to send
|
:param message: byte string to send
|
||||||
"""
|
"""
|
||||||
data = self.header_unencrypted(message) + message
|
|
||||||
step1 = struct.pack('<II', len(data)+12, self.number) + data
|
message_id = struct.pack('<Q', int(time()*2**30)*4)
|
||||||
|
|
||||||
|
if self.auth_key is None or self.server_salt is None:
|
||||||
|
# Unencrypted data send
|
||||||
|
message = (b'\x00\x00\x00\x00\x00\x00\x00\x00' +
|
||||||
|
message_id +
|
||||||
|
struct.pack('<I', len(message_data)) +
|
||||||
|
message_data)
|
||||||
|
else:
|
||||||
|
# Encrypted data send
|
||||||
|
encrypted_data = (self.server_salt +
|
||||||
|
self.session_id +
|
||||||
|
message_id +
|
||||||
|
struct.pack('<II', self.number, len(message_data)) +
|
||||||
|
message_data)
|
||||||
|
message_key = SHA.new(encrypted_data).digest()[-16:]
|
||||||
|
padding = b"\x00" * (-len(encrypted_data) % 16)
|
||||||
|
aes_key, aes_iv = self.aes_calculate(message_key)
|
||||||
|
|
||||||
|
message = (self.auth_key_id + message_key +
|
||||||
|
crypt.ige_encrypt(encrypted_data+padding, aes_key, aes_iv))
|
||||||
|
|
||||||
|
step1 = struct.pack('<II', len(message)+12, self.number) + message
|
||||||
step2 = step1 + struct.pack('<I', crc32(step1))
|
step2 = step1 + struct.pack('<I', crc32(step1))
|
||||||
self.sock.send(step2)
|
self.sock.send(step2)
|
||||||
self.number += 1
|
self.number += 1
|
||||||
# Sending message visualisation to console
|
|
||||||
# print('>>')
|
|
||||||
# vis(step2)
|
|
||||||
|
|
||||||
def recv_message(self):
|
def recv_message(self):
|
||||||
"""
|
"""
|
||||||
@ -111,30 +96,42 @@ class Session:
|
|||||||
"""
|
"""
|
||||||
packet_length_data = self.sock.recv(4) # reads how many bytes to read
|
packet_length_data = self.sock.recv(4) # reads how many bytes to read
|
||||||
|
|
||||||
if len(packet_length_data) > 0: # if we have smth. in the socket
|
if len(packet_length_data) < 4:
|
||||||
packet_length = struct.unpack("<I", packet_length_data)[0]
|
|
||||||
packet = self.sock.recv(packet_length - 4) # read the rest of bytes from socket
|
|
||||||
(x, auth_key_id, message_id, message_length)= struct.unpack("<I8s8sI", packet[0:24])
|
|
||||||
data = packet[24:24+message_length]
|
|
||||||
crc = packet[-4:]
|
|
||||||
# Checking the CRC32 correctness of received data
|
|
||||||
if crc32(packet_length_data + packet[0:-4]) == struct.unpack('<I', crc)[0]:
|
|
||||||
return data
|
|
||||||
else:
|
|
||||||
raise Exception("CRC32 was not correct!")
|
|
||||||
else:
|
|
||||||
raise Exception("Nothing in the socket!")
|
raise Exception("Nothing in the socket!")
|
||||||
|
packet_length = struct.unpack("<I", packet_length_data)[0]
|
||||||
|
packet = self.sock.recv(packet_length - 4) # read the rest of bytes from socket
|
||||||
|
|
||||||
|
# check the CRC32
|
||||||
|
if not crc32(packet_length_data + packet[0:-4]) == struct.unpack('<I', packet[-4:])[0]:
|
||||||
|
raise Exception("CRC32 was not correct!")
|
||||||
|
x = struct.unpack("<I", packet[:4])
|
||||||
|
auth_key_id = packet[4:12]
|
||||||
|
if auth_key_id == b'\x00\x00\x00\x00\x00\x00\x00\x00':
|
||||||
|
# No encryption - Plain text
|
||||||
|
(message_id, message_length) = struct.unpack("<8sI", packet[12:24])
|
||||||
|
data = packet[24:24+message_length]
|
||||||
|
elif auth_key_id == self.auth_key_id:
|
||||||
|
print("WOW! Encrypted data!")
|
||||||
|
message_key = packet[12:28]
|
||||||
|
encrypted_data = packet[28:-4]
|
||||||
|
aes_key, aes_iv = self.aes_calculate(message_key, direction="from server")
|
||||||
|
decrypted_data = crypt.ige_decrypt(encrypted_data, aes_key, aes_iv)
|
||||||
|
vis(decrypted_data)
|
||||||
|
assert decrypted_data[0:8] == self.server_salt
|
||||||
|
assert decrypted_data[8:16] == self.session_id
|
||||||
|
message_id = decrypted_data[16:24]
|
||||||
|
seq_no = struct.unpack("<I", decrypted_data[24:28])[0]
|
||||||
|
message_data_length = struct.unpack("<I", decrypted_data[28:32])[0]
|
||||||
|
data = decrypted_data[32:32+message_data_length]
|
||||||
|
else:
|
||||||
|
raise Exception("Got unknown auth_key id")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def method_call(self, method, **kwargs):
|
def method_call(self, method, **kwargs):
|
||||||
z=io.BytesIO()
|
z = io.BytesIO()
|
||||||
TL.serialize_method(z, method, **kwargs)
|
TL.serialize_method(z, method, **kwargs)
|
||||||
# z.getvalue() on py2.7 returns str, which means bytes
|
|
||||||
# on py3.4 returns bytes
|
|
||||||
# bytearray is closer to the same data type to be shared
|
|
||||||
z_val = bytearray(z.getvalue())
|
z_val = bytearray(z.getvalue())
|
||||||
# print("z_val: " + z_val.__repr__())
|
|
||||||
# print("z_val type: " + str(type(z_val)))
|
|
||||||
# print("len of z_val: " + str(len(z_val)))
|
|
||||||
self.send_message(z_val)
|
self.send_message(z_val)
|
||||||
server_answer = self.recv_message()
|
server_answer = self.recv_message()
|
||||||
return TL.deserialize(io.BytesIO(server_answer))
|
return TL.deserialize(io.BytesIO(server_answer))
|
||||||
@ -151,9 +148,8 @@ class Session:
|
|||||||
public_key_fingerprint = ResPQ['server_public_key_fingerprints'][0]
|
public_key_fingerprint = ResPQ['server_public_key_fingerprints'][0]
|
||||||
|
|
||||||
pq_bytes = ResPQ['pq']
|
pq_bytes = ResPQ['pq']
|
||||||
|
pq = bytes_to_long(pq_bytes)
|
||||||
|
|
||||||
# TODO: from_bytes here
|
|
||||||
pq = struct.unpack('>q', pq_bytes)[0]
|
|
||||||
[p, q] = prime.primefactors(pq)
|
[p, q] = prime.primefactors(pq)
|
||||||
if p > q: (p, q) = (q, p)
|
if p > q: (p, q) = (q, p)
|
||||||
assert p*q == pq and p < q
|
assert p*q == pq and p < q
|
||||||
@ -243,7 +239,6 @@ class Session:
|
|||||||
auth_key = pow(g_a, b, dh_prime)
|
auth_key = pow(g_a, b, dh_prime)
|
||||||
auth_key_str = long_to_bytes(auth_key)
|
auth_key_str = long_to_bytes(auth_key)
|
||||||
auth_key_sha = SHA.new(auth_key_str).digest()
|
auth_key_sha = SHA.new(auth_key_str).digest()
|
||||||
auth_key_hash = auth_key_sha[-8:]
|
|
||||||
auth_key_aux_hash = auth_key_sha[:8]
|
auth_key_aux_hash = auth_key_sha[:8]
|
||||||
|
|
||||||
new_nonce_hash1 = SHA.new(new_nonce+b'\x01'+auth_key_aux_hash).digest()[-16:]
|
new_nonce_hash1 = SHA.new(new_nonce+b'\x01'+auth_key_aux_hash).digest()[-16:]
|
||||||
@ -257,6 +252,7 @@ class Session:
|
|||||||
|
|
||||||
self.server_salt = strxor(new_nonce[0:8], server_nonce[0:8])
|
self.server_salt = strxor(new_nonce[0:8], server_nonce[0:8])
|
||||||
self.auth_key = auth_key_str
|
self.auth_key = auth_key_str
|
||||||
|
self.auth_key_id = auth_key_sha[-8:]
|
||||||
print("Auth key generated")
|
print("Auth key generated")
|
||||||
|
|
||||||
def aes_calculate(self, msg_key, direction="to server"):
|
def aes_calculate(self, msg_key, direction="to server"):
|
||||||
|
@ -20,3 +20,10 @@ ip = config.get('App data', 'ip_address')
|
|||||||
port = config.getint('App data', 'port')
|
port = config.getint('App data', 'port')
|
||||||
|
|
||||||
Session = mtproto.Session(ip, port)
|
Session = mtproto.Session(ip, port)
|
||||||
|
Session.create_auth_key()
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
f = Session.method_call('ping', ping_id=i)
|
||||||
|
print(f)
|
||||||
|
i += 1
|
Loading…
Reference in New Issue
Block a user