Could not extract type: vector<%Message>

This commit is contained in:
Anton Grigoryev 2015-03-17 19:28:24 +03:00
parent de05aad138
commit 32b99a298c
4 changed files with 77 additions and 72 deletions

7
TL.py
View File

@ -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)

View File

@ -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)")

View File

@ -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"):

View File

@ -19,4 +19,11 @@ if not config.read('credentials'):
ip = config.get('App data', 'ip_address') 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