I tried to implement telegram api with python.
base code forked form https://github.com/datamachine/twx
and I want to add checkPhone and sendCode feature, but server always returns result that cannot be understood.
here is my code
import os
import struct
from io import BytesIO
from Crypto.Hash import SHA
from twx.mtproto.mtproto import MTProto as MTProtoParent, crc32
from twx.mtproto import rpc
from twx.mtproto import crypt
from hexdump import hexdump
class sendCode(rpc.TLObject):
constructor = 0x768d5f4d
def __init__(self, phone_number, sms_type, api_id, api_hash, lang_code):
self.phone_number = phone_number # bytes
self.sms_type = sms_type # integer
self.api_id = int(api_id) # integer
self.api_hash = api_hash # string
self.lang_code = lang_code # string
def get_bytes(self):
phone_number_io = BytesIO()
rpc.serialize_string(phone_number_io, self.phone_number)
params_io = BytesIO()
rpc.serialize_string(params_io, self.api_hash.encode())
rpc.serialize_string(params_io, self.lang_code.encode())
return struct.pack("<I16sII40s", sendCode.constructor, phone_number_io.getvalue(), self.sms_type, self.api_id, params_io.getvalue())
class MTProto(MTProtoParent):
def __init__(self, api_secret, api_id):
MTProtoParent.__init__(self, api_secret, api_id)
def get_encrypted_message(self, message_data, debug=False):
salt = self.dc.server_salt
session_id = os.urandom(8)
message_id = self.dc.generate_message_id()
seq_no = self.dc.number
payload = message_data
to_be_encrypted = struct.pack('<8s8sQHI', salt, session_id, message_id, seq_no, len(payload)) + payload
to_be_encrypted_padded = to_be_encrypted + os.urandom(-len(to_be_encrypted) % 16)
if debug:
print("\nin get encrypted message")
print("\nto be encrypted data:")
hexdump(to_be_encrypted_padded)
msg_key = SHA.new(to_be_encrypted_padded).digest()[-16:]
auth_key_id = self.dc.auth_key_id
auth_key = self.dc.auth_key
# to get 256bit AES Key and AES IV
sha1_a = SHA.new(msg_key + auth_key[0:32]).digest()
sha1_b = SHA.new(auth_key[32:48] + msg_key + auth_key[48:64]).digest()
sha1_c = SHA.new(auth_key[64:96] + msg_key).digest()
sha1_d = SHA.new(msg_key + auth_key[96:128]).digest()
aes_key = sha1_a[0:8] + sha1_b[8:20] + sha1_c[4:16]
aes_iv = sha1_a[8:20] + sha1_b[0:8] + sha1_c[16:20] + sha1_d[0:8]
# generate encrypted data
encrypted_data = crypt.ige_encrypt(to_be_encrypted_padded, aes_key, aes_iv)
message = struct.pack('<8s16s', auth_key_id, msg_key) + encrypted_data
if debug:
print("\nencrypted data:")
hexdump(message)
return message
def send_encrypted(self, message_data, debug=False):
message = self.get_encrypted_message(message_data, debug=False)
# message = struct.pack('<II', len(message) + 12, self.dc.number) + message
message = struct.pack('<II', len(message) + 12, self.dc.number) + message
msg_chksum = crc32(message)
message += struct.pack('<I', msg_chksum)
if debug:
hexdump(message)
self.dc.sock.send(message)
self.dc.number += 1
# self.dc.send_message(message)
def checkPhone(self, phone_number):
bytes_io = BytesIO()
rpc.serialize_string(bytes_io, phone_number)
query = rpc.checkPhone(bytes_io.getvalue()).get_bytes()
# hexdump(query)
self.send_encrypted(query, debug=True)
# self.dc.send_message(query)
print(rpc.checkedPhone().set_params(self.dc.recv_message()))
def sendCode(self, phone_number, sms_type, lang_code):
query = sendCode(phone_number, sms_type, self.api_id, self.api_secret, lang_code).get_bytes()
# hexdump(query)
self.send_encrypted(query, debug=True)
# self.dc.send_message(query)
self.dc.recv_message(debug=True)
if __name__ == "__main__":
mtp = MTProto("api_secret", "api_key")
mtp.dc.create_auth_key()
mtp.checkPhone(b"821011112222")
and server always responds as below, regardless what auth_key_id is,
00000000: 10 00 00 00 03 00 00 00 6C FE FF FF EE 28 CE 89
what is wrong with my code?