def send_message(self, message_data): """ Forming the message frame and sending message to server :param message: byte string to send """ 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 = os.urandom((-len(encrypted_data)) % 16) print(len(encrypted_data + padding)) 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)) self.sock.send(step2) self.number += 1
def send_message(self, message_data): """ Forming the message frame and sending message to server :param message: byte string to send """ message_id = struct.pack('<Q', int((time()+self.timedelta)*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 = os.urandom((-len(encrypted_data)) % 16) print(len(encrypted_data+padding)) 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)) self.sock.send(step2) self.number += 1
def send_message(self, message_data): """ Forming the message frame and sending message to server :param message: byte string to send """ message_id = pack('<Q', int((time() + self.timedelta) * 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 + pack('<I', len(message_data)) + message_data) else: # Encrypted data send encrypted_data = (self.server_salt + self.session_id + message_id + pack('<II', self.number, len(message_data)) + message_data) r = randint(12, 1024 - 16) encrypted_data += urandom(r - ((r + len(encrypted_data)) % 16)) message_key = SHA256.new(self.auth_key[88:88 + 32] + encrypted_data).digest()[8:8 + 16] aes_key, aes_iv = self.KDF2(message_key) message = (self.auth_key_id + message_key + crypt.ige_encrypt(encrypted_data, aes_key, aes_iv)) if self.padded: # Use Padded intermediate instead padding = urandom(randint(12, 1024)) # MTProto 2.0 self.sock.send( pack("<I", len(padding) + len(message)) + message + padding) elif self.intermediate: self.sock.send(pack("<I", len(message)) + message) else: len_div4 = int(len(message) / 4) if len_div4 > 127: abridged_pack = pack("<I", (len_div4 << 8) + 0x7f) else: abridged_pack = pack("<B", len_div4) abridged_pack += message self.sock.send(abridged_pack)
def create_auth_key(self): nonce = os.urandom(16) print("Requesting pq") ResPQ = self.method_call('req_pq', nonce=nonce) server_nonce = ResPQ['server_nonce'] # TODO: selecting RSA public key based on this fingerprint public_key_fingerprint = ResPQ['server_public_key_fingerprints'][0] pq_bytes = ResPQ['pq'] pq = bytes_to_long(pq_bytes) [p, q] = prime.primefactors(pq) if p > q: (p, q) = (q, p) assert p * q == pq and p < q print("Factorization %d = %d * %d" % (pq, p, q)) p_bytes = long_to_bytes(p) q_bytes = long_to_bytes(q) f = open(os.path.join(os.path.dirname(__file__), "rsa.pub")) key = RSA.importKey(f.read()) new_nonce = os.urandom(32) data = TL.serialize_obj('p_q_inner_data', pq=pq_bytes, p=p_bytes, q=q_bytes, nonce=nonce, server_nonce=server_nonce, new_nonce=new_nonce) sha_digest = SHA.new(data).digest() random_bytes = os.urandom(255 - len(data) - len(sha_digest)) to_encrypt = sha_digest + data + random_bytes encrypted_data = key.encrypt(to_encrypt, 0)[0] print("Starting Diffie Hellman key exchange") server_dh_params = self.method_call( 'req_DH_params', nonce=nonce, server_nonce=server_nonce, p=p_bytes, q=q_bytes, public_key_fingerprint=public_key_fingerprint, encrypted_data=encrypted_data) assert nonce == server_dh_params['nonce'] assert server_nonce == server_dh_params['server_nonce'] encrypted_answer = server_dh_params['encrypted_answer'] tmp_aes_key = SHA.new(new_nonce + server_nonce).digest() + SHA.new( server_nonce + new_nonce).digest()[0:12] tmp_aes_iv = SHA.new(server_nonce + new_nonce).digest( )[12:20] + SHA.new(new_nonce + new_nonce).digest() + new_nonce[0:4] answer_with_hash = crypt.ige_decrypt(encrypted_answer, tmp_aes_key, tmp_aes_iv) answer_hash = answer_with_hash[:20] answer = answer_with_hash[20:] # TODO: SHA hash assertion here server_DH_inner_data = TL.deserialize(io.BytesIO(answer)) assert nonce == server_DH_inner_data['nonce'] assert server_nonce == server_DH_inner_data['server_nonce'] dh_prime_str = server_DH_inner_data['dh_prime'] g = server_DH_inner_data['g'] g_a_str = server_DH_inner_data['g_a'] server_time = server_DH_inner_data['server_time'] self.timedelta = server_time - time() print("Server-client time delta = %.1f s" % self.timedelta) dh_prime = bytes_to_long(dh_prime_str) g_a = bytes_to_long(g_a_str) assert prime.isprime(dh_prime) retry_id = 0 b_str = os.urandom(256) b = bytes_to_long(b_str) g_b = pow(g, b, dh_prime) g_b_str = long_to_bytes(g_b) data = TL.serialize_obj('client_DH_inner_data', nonce=nonce, server_nonce=server_nonce, retry_id=retry_id, g_b=g_b_str) data_with_sha = SHA.new(data).digest() + data data_with_sha_padded = data_with_sha + os.urandom( -len(data_with_sha) % 16) encrypted_data = crypt.ige_encrypt(data_with_sha_padded, tmp_aes_key, tmp_aes_iv) Set_client_DH_params_answer = self.method_call( 'set_client_DH_params', nonce=nonce, server_nonce=server_nonce, encrypted_data=encrypted_data) auth_key = pow(g_a, b, dh_prime) auth_key_str = long_to_bytes(auth_key) auth_key_sha = SHA.new(auth_key_str).digest() 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_hash2 = SHA.new(new_nonce + b'\x02' + auth_key_aux_hash).digest()[-16:] new_nonce_hash3 = SHA.new(new_nonce + b'\x03' + auth_key_aux_hash).digest()[-16:] assert Set_client_DH_params_answer['nonce'] == nonce assert Set_client_DH_params_answer['server_nonce'] == server_nonce assert Set_client_DH_params_answer[ 'new_nonce_hash1'] == new_nonce_hash1 print("Diffie Hellman key exchange processed successfully") self.server_salt = strxor(new_nonce[0:8], server_nonce[0:8]) self.auth_key = auth_key_str self.auth_key_id = auth_key_sha[-8:] print("Auth key generated")
def create_auth_key(self): nonce = os.urandom(16) print("Requesting pq") ResPQ = self.method_call('req_pq', nonce=nonce) server_nonce = ResPQ['server_nonce'] # TODO: selecting RSA public key based on this fingerprint public_key_fingerprint = ResPQ['server_public_key_fingerprints'][0] pq_bytes = ResPQ['pq'] pq = bytes_to_long(pq_bytes) [p, q] = prime.primefactors(pq) if p > q: (p, q) = (q, p) assert p*q == pq and p < q print("Factorization %d = %d * %d" % (pq, p, q)) p_bytes = long_to_bytes(p) q_bytes = long_to_bytes(q) f = open(os.path.join(os.path.dirname(__file__), "rsa.pub")) key = RSA.importKey(f.read()) new_nonce = os.urandom(32) data = TL.serialize_obj('p_q_inner_data', pq=pq_bytes, p=p_bytes, q=q_bytes, nonce=nonce, server_nonce=server_nonce, new_nonce=new_nonce) sha_digest = SHA.new(data).digest() random_bytes = os.urandom(255-len(data)-len(sha_digest)) to_encrypt = sha_digest + data + random_bytes encrypted_data = key.encrypt(to_encrypt, 0)[0] print("Starting Diffie Hellman key exchange") server_dh_params = self.method_call('req_DH_params', nonce=nonce, server_nonce=server_nonce, p=p_bytes, q=q_bytes, public_key_fingerprint=public_key_fingerprint, encrypted_data=encrypted_data) assert nonce == server_dh_params['nonce'] assert server_nonce == server_dh_params['server_nonce'] encrypted_answer = server_dh_params['encrypted_answer'] tmp_aes_key = SHA.new(new_nonce + server_nonce).digest() + SHA.new(server_nonce + new_nonce).digest()[0:12] tmp_aes_iv = SHA.new(server_nonce + new_nonce).digest()[12:20] + SHA.new(new_nonce + new_nonce).digest() + new_nonce[0:4] answer_with_hash = crypt.ige_decrypt(encrypted_answer, tmp_aes_key, tmp_aes_iv) answer_hash = answer_with_hash[:20] answer = answer_with_hash[20:] # TODO: SHA hash assertion here server_DH_inner_data = TL.deserialize(io.BytesIO(answer)) assert nonce == server_DH_inner_data['nonce'] assert server_nonce == server_DH_inner_data['server_nonce'] dh_prime_str = server_DH_inner_data['dh_prime'] g = server_DH_inner_data['g'] g_a_str = server_DH_inner_data['g_a'] server_time = server_DH_inner_data['server_time'] self.timedelta = server_time - time() print("Server-client time delta = %.1f s" % self.timedelta) dh_prime = bytes_to_long(dh_prime_str) g_a = bytes_to_long(g_a_str) assert prime.isprime(dh_prime) retry_id = 0 b_str = os.urandom(256) b = bytes_to_long(b_str) g_b = pow(g, b, dh_prime) g_b_str = long_to_bytes(g_b) data = TL.serialize_obj('client_DH_inner_data', nonce=nonce, server_nonce=server_nonce, retry_id=retry_id, g_b=g_b_str) data_with_sha = SHA.new(data).digest()+data data_with_sha_padded = data_with_sha + os.urandom(-len(data_with_sha) % 16) encrypted_data = crypt.ige_encrypt(data_with_sha_padded, tmp_aes_key, tmp_aes_iv) for i in range(1, self.AUTH_MAX_RETRY): # retry when dh_gen_retry or dh_gen_fail Set_client_DH_params_answer = self.method_call('set_client_DH_params', nonce=nonce, server_nonce=server_nonce, encrypted_data=encrypted_data) # print Set_client_DH_params_answer auth_key = pow(g_a, b, dh_prime) auth_key_str = long_to_bytes(auth_key) auth_key_sha = SHA.new(auth_key_str).digest() 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_hash2 = SHA.new(new_nonce+b'\x02'+auth_key_aux_hash).digest()[-16:] new_nonce_hash3 = SHA.new(new_nonce+b'\x03'+auth_key_aux_hash).digest()[-16:] assert Set_client_DH_params_answer['nonce'] == nonce assert Set_client_DH_params_answer['server_nonce'] == server_nonce if Set_client_DH_params_answer.name == 'dh_gen_ok': assert Set_client_DH_params_answer['new_nonce_hash1'] == new_nonce_hash1 print("Diffie Hellman key exchange processed successfully") self.server_salt = strxor(new_nonce[0:8], server_nonce[0:8]) self.auth_key = auth_key_str self.auth_key_id = auth_key_sha[-8:] print("Auth key generated") return "Auth Ok" elif Set_client_DH_params_answer.name == 'dh_gen_retry': assert Set_client_DH_params_answer['new_nonce_hash2'] == new_nonce_hash2 print ("Retry Auth") elif Set_client_DH_params_answer.name == 'dh_gen_fail': assert Set_client_DH_params_answer['new_nonce_hash3'] == new_nonce_hash3 print("Auth Failed") raise Exception("Auth Failed") else: raise Exception("Response Error")